Compare commits

...

28 Commits

Author SHA1 Message Date
Fatimah.Alshammari 8ae81d2b24 fixed parking qr 2 months ago
Fatimah.Alshammari 11de01e079 Merge branch 'refs/heads/master' into fatima
# Conflicts:
#	lib/presentation/hmg_services/services_page.dart
#	lib/routes/app_routes.dart
#	lib/theme/colors.dart
2 months ago
Fatimah.Alshammari 62bb667cb8 added parking part 2 months ago
Haroon6138 58b1e3a386 Merge pull request 'dev_aamir' (#133) from dev_aamir into master
Reviewed-on: https://34.17.182.140/Haroon6138/HMG_Patient_App_New/pulls/133
2 months ago
aamir-csol 6c1707128b Merge branch 'master' into dev_aamir 2 months ago
aamir-csol 3601e39a29 Health calculators & converters 2 months ago
Haroon6138 e0606ceb65 Merge pull request 'dev_sultan' (#132) from dev_sultan into master
Reviewed-on: https://34.17.182.140/Haroon6138/HMG_Patient_App_New/pulls/132
2 months ago
Sultan khan 6ceee6973f Merge branch 'master' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New into dev_sultan
* 'master' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New:
  sickleave fix
  Health calculators & converters
  updates
  updates
  updates
  updates
  Nearest Appointment selected

# Conflicts:
#	lib/presentation/lab/lab_orders_page.dart
2 months ago
Sultan khan 68a34369ee no message 2 months ago
Sultan khan 873b2a1591 sorting done, covid inprogress 2 months ago
Haroon6138 37574412a5 Merge pull request 'haroon_dev' (#130) from haroon_dev into master
Reviewed-on: https://34.17.182.140/Haroon6138/HMG_Patient_App_New/pulls/130
2 months ago
haroon amjad b353de3cf8 Merge branch 'master' into haroon_dev
# Conflicts:
#	lib/presentation/book_appointment/select_doctor_page.dart
#	lib/presentation/book_appointment/widgets/doctor_card.dart
2 months ago
haroon amjad dcdd17f0e9 sickleave fix 2 months ago
Haroon6138 8b90a84adf Merge pull request 'dev_aamir' (#129) from dev_aamir into master
Reviewed-on: https://34.17.182.140/Haroon6138/HMG_Patient_App_New/pulls/129
2 months ago
aamir-csol 54f8391066 Merge branch 'master' into dev_aamir
# Conflicts:
#	lib/presentation/lab/lab_orders_page.dart
2 months ago
aamir-csol 135b05e561 Health calculators & converters 2 months ago
haroon amjad 510d82b592 Merge branch 'master' into haroon_dev 2 months ago
Haroon6138 e1a021129b Merge pull request 'dev_sultan' (#128) from dev_sultan into master
Reviewed-on: https://34.17.182.140/Haroon6138/HMG_Patient_App_New/pulls/128
2 months ago
Sultan khan 366429f083 Merge branch 'master' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New into dev_sultan
* 'master' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New:
  Health calculators & converters
  Health calculators & converters
  collapsing_list_view improvement

# Conflicts:
#	lib/widgets/appbar/collapsing_list_view.dart
2 months ago
haroon amjad 3cc2610d30 updates 2 months ago
haroon amjad f886357bcd updates 2 months ago
Sultan khan 40638cfec4 Merge branch 'master' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New into dev_sultan
* 'master' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New:
  collapsing_list_view improvement
  Updated design for HHC
  updates & fixes
  QLine API implementation enhancements
  LiveCare flow from clinic selection implemented

# Conflicts:
#	lib/presentation/home/landing_page.dart
2 months ago
haroon amjad 5581346a2d updates 3 months ago
haroon amjad b8ecb70aa2 Merge branch 'master' into haroon_dev 3 months ago
haroon amjad 15f5eaf8e5 updates 3 months ago
haroon amjad 87fd9a42b7 Nearest Appointment selected 3 months ago
Sultan khan d28c94606b Merge branch 'master' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New into dev_sultan
* 'master' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New:
  updates
  updates
  updates & fixes
3 months ago
Sultan khan 68e1b116f9 last rated updated. 3 months ago

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32.381" height="37.391" viewBox="0 0 32.381 37.391">
<g id="affiliate-marketing_1_" data-name="affiliate-marketing (1)" transform="translate(-34.298 0)">
<path id="Path_4982" data-name="Path 4982" d="M63.419,23.145l.415,1.991-2.684-1.55a11.718,11.718,0,0,0,0-9.782l2.684-1.55-.415,1.991,2.145.447,1.115-5.345L61.492,7.641l-.685,2.081,1.932.636-2.687,1.551a11.736,11.736,0,0,0-8.469-4.893v-3.1L53.1,5.272l1.46-1.634L50.489,0,46.417,3.639l1.46,1.634,1.517-1.355v3.1a11.736,11.736,0,0,0-8.469,4.893l-2.687-1.551,1.932-.636-.685-2.081L34.3,9.348l1.115,5.346,2.145-.447-.415-1.991,2.684,1.55a11.718,11.718,0,0,0,0,9.782l-2.684,1.55.415-1.991L35.413,22.7,34.3,28.043l5.187,1.707.685-2.081-1.932-.636,2.687-1.551a11.736,11.736,0,0,0,8.469,4.893v3.1l-1.517-1.355-1.46,1.634,4.072,3.639,4.072-3.639L53.1,32.118l-1.517,1.355v-3.1a11.736,11.736,0,0,0,8.469-4.893l2.687,1.551-1.932.636.685,2.081,5.187-1.707L65.564,22.7Z" fill="#2e303a"/>
<g id="user_24_" data-name="user (24)" transform="translate(42.096 9.104)">
<path id="Path_4983" data-name="Path 4983" d="M10.267,8.536a3.028,3.028,0,0,0,1.792.581,3.125,3.125,0,0,0,3.059-3.059,3.059,3.059,0,1,0-4.85,2.478Z" transform="translate(-3.378 0)" fill="#fff"/>
<path id="Path_4984" data-name="Path 4984" d="M11.372,16.55a3.919,3.919,0,0,1-5.384,0,5.689,5.689,0,0,0-2.989,5,.438.438,0,0,0,.437.437H13.924a.438.438,0,0,0,.437-.437A5.689,5.689,0,0,0,11.372,16.55Z" transform="translate(0 -7.629)" fill="#fff"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,7 @@
<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="M0.462872 10.1666C0.228367 10.108 0.111115 10.0787 0.0468218 9.98619C-0.0174712 9.8937 -0.00458142 9.77798 0.0211982 9.54656C0.619456 4.17595 5.17435 0 10.7046 0C16.2349 0 20.7898 4.176 21.388 9.54665C21.4138 9.77807 21.4267 9.89379 21.3624 9.98628C21.2981 10.0788 21.1808 10.1081 20.9463 10.1667L16.5365 11.2692C16.3156 11.3244 16.2052 11.352 16.1187 11.3202C16.0321 11.2885 15.9488 11.1726 15.7824 10.9408C15.5865 10.6681 15.3355 10.4575 15.0448 10.3067C14.5775 10.0644 14.0546 10.0002 13.5871 10.0002H7.82242C7.35432 10.0002 6.83087 10.0645 6.3633 10.3073C6.07298 10.458 5.82232 10.6685 5.62668 10.9408C5.46023 11.1725 5.37701 11.2884 5.29044 11.3201C5.20386 11.3519 5.09347 11.3243 4.87268 11.2691L0.462872 10.1666ZM5.67426 7.21967C6.13506 6.75887 6.76003 6.5 7.4117 6.5H8.70459C9.1188 6.5 9.45459 6.83579 9.45459 7.25C9.45459 7.66421 9.1188 8 8.70459 8H7.4117C7.15786 8 6.91441 8.10084 6.73492 8.28033C6.44203 8.57322 5.96715 8.57322 5.67426 8.28033C5.38137 7.98744 5.38137 7.51256 5.67426 7.21967ZM11.9546 7.25C11.9546 6.83579 12.2904 6.5 12.7046 6.5H13.9975C14.6491 6.5 15.2741 6.75887 15.7349 7.21967C16.0278 7.51256 16.0278 7.98744 15.7349 8.28033C15.442 8.57322 14.9672 8.57322 14.6743 8.28033C14.4948 8.10084 14.2513 8 13.9975 8H12.7046C12.2904 8 11.9546 7.66421 11.9546 7.25Z" fill="white"/>
<path d="M0.0837141 12.4212C0.0267055 12.0559 -0.00179879 11.8733 0.117089 11.7647C0.235976 11.6561 0.422465 11.7027 0.795442 11.7959L4.52364 12.728C4.76706 12.7888 4.88877 12.8193 4.95293 12.9082C5.01708 12.9972 5.00713 13.1275 4.98722 13.3883C4.9545 13.8168 4.94652 14.2577 4.96329 14.6923C4.97667 15.0388 4.98336 15.2121 4.88525 15.3055C4.78714 15.399 4.62125 15.3844 4.28947 15.3554L1.20555 15.0852C1.031 15.0699 0.943724 15.0622 0.875524 15.0115C0.807325 14.9608 0.776105 14.8824 0.713667 14.7256C0.422134 13.9936 0.208718 13.222 0.0837141 12.4212Z" fill="white"/>
<path d="M2.94493 16.749C2.40172 16.7002 2.13012 16.6758 2.03715 16.8534C1.94417 17.0309 2.11204 17.232 2.44777 17.6343C4.4197 19.9968 7.3866 21.5 10.7046 21.5C14.0226 21.5 16.9895 19.9968 18.9614 17.6343C19.2971 17.2321 19.465 17.031 19.372 16.8534C19.279 16.6759 19.0074 16.7002 18.4642 16.749L16.4748 16.9275C16.2986 16.9434 16.2105 16.9513 16.1473 16.993C16.0842 17.0348 16.033 17.1302 15.9308 17.3211C15.7212 17.7122 15.4152 18.0015 15.0448 18.1936C14.5775 18.436 14.0546 18.5002 13.5871 18.5002H7.82241C7.35508 18.5002 6.83229 18.436 6.36514 18.1938C5.99468 18.0017 5.68855 17.7124 5.47892 17.3211C5.37667 17.1303 5.32555 17.0349 5.26236 16.9931C5.19916 16.9513 5.11106 16.9434 4.93485 16.9276L2.94493 16.749Z" fill="white"/>
<path d="M20.6955 14.7257C20.6331 14.8824 20.6018 14.9608 20.5336 15.0115C20.4654 15.0623 20.3782 15.0699 20.2036 15.0852L17.1201 15.3554C16.7883 15.3844 16.6225 15.399 16.5244 15.3055C16.4263 15.2121 16.4329 15.0388 16.4463 14.6923C16.463 14.2578 16.455 13.8169 16.4222 13.3884C16.4023 13.1276 16.3923 12.9972 16.4564 12.9083C16.5206 12.8193 16.6423 12.7889 16.8858 12.728L20.6137 11.796C20.9867 11.7028 21.1732 11.6562 21.2921 11.7648C21.411 11.8734 21.3825 12.056 21.3255 12.4213C21.2004 13.2221 20.987 13.9937 20.6955 14.7257Z" fill="white"/>
<path d="M6.73398 12.051C6.80458 11.8189 6.91733 11.7098 7.05454 11.6385C7.22042 11.5524 7.47075 11.5002 7.82242 11.5002H13.5871C13.9382 11.5002 14.1884 11.5523 14.3542 11.6383C14.4914 11.7095 14.604 11.8184 14.6746 12.0497C15.0482 13.274 15.0482 15.2264 14.6746 16.4506C14.604 16.6819 14.4914 16.7909 14.3542 16.8621C14.1884 16.9481 13.9382 17.0002 13.5871 17.0002H7.82241C7.47147 17.0002 7.22139 16.9481 7.05557 16.8621C6.91833 16.791 6.80577 16.6821 6.73521 16.451C6.36164 15.2274 6.36134 13.2759 6.73398 12.051Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

@ -0,0 +1 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 391 B

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30.776" height="24.337" viewBox="0 0 30.776 24.337">
<g id="folder" transform="translate(0 -42.679)">
<g id="Group_8637" data-name="Group 8637" transform="translate(0 45.895)">
<g id="Group_8636" data-name="Group 8636" transform="translate(0 0)">
<path id="Path_5021" data-name="Path 5021" d="M25.043,88.08H13.94l-2.209-2.625a.377.377,0,0,0-.3-.136H2.471A2.489,2.489,0,0,0,0,87.808v16.145a2.489,2.489,0,0,0,2.486,2.486H25.043a2.489,2.489,0,0,0,2.486-2.486V90.566A2.489,2.489,0,0,0,25.043,88.08Z" transform="translate(0 -85.319)" fill="#2bb8a6"/>
</g>
</g>
<g id="Group_8639" data-name="Group 8639" transform="translate(3.274 42.679)">
<g id="Group_8638" data-name="Group 8638" transform="translate(0 0)">
<path id="Path_5022" data-name="Path 5022" d="M68.431,45.44H57.324l-2.209-2.625a.377.377,0,0,0-.3-.136H45.859A2.486,2.486,0,0,0,43.4,44.827h8.168a1.445,1.445,0,0,1,1.11.516l1.886,2.242H65.169a3.56,3.56,0,0,1,3.554,3.554V63.775A2.486,2.486,0,0,0,70.9,61.31V47.923A2.489,2.489,0,0,0,68.431,45.44Z" transform="translate(-43.4 -42.679)" fill="#2bb8a6"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30.996" height="28.414" viewBox="0 0 30.996 28.414">
<g id="image-gallery" transform="translate(-0.001 -2)">
<g id="Group_8635" data-name="Group 8635" transform="translate(0.001 5.875)">
<g id="Group_8634" data-name="Group 8634" transform="translate(0 5.078)">
<path id="Path_5018" data-name="Path 5018" d="M8.072,22.581A4.836,4.836,0,0,1,3.5,19.286l-.045-.149a4.711,4.711,0,0,1-.223-1.4V8.932L.1,19.391a2.933,2.933,0,0,0,2.056,3.558L22.123,28.3a2.962,2.962,0,0,0,.744.1,2.88,2.88,0,0,0,2.791-2.112l1.164-3.7Z" transform="translate(-0.001 -8.932)" fill="#2bb8a6"/>
</g>
<path id="Path_5019" data-name="Path 5019" d="M9.583,10.166A2.583,2.583,0,1,0,7,7.583,2.586,2.586,0,0,0,9.583,10.166Z" transform="translate(2.04 -5)" fill="#2bb8a6"/>
</g>
<path id="Path_5020" data-name="Path 5020" d="M26.6,2H7.229A3.233,3.233,0,0,0,4,5.229V19.436a3.233,3.233,0,0,0,3.229,3.229H26.6a3.233,3.233,0,0,0,3.229-3.229V5.229A3.233,3.233,0,0,0,26.6,2ZM7.229,4.583H26.6a.646.646,0,0,1,.646.646V14.4l-4.08-4.761a2.313,2.313,0,0,0-1.732-.794,2.258,2.258,0,0,0-1.726.815l-4.8,5.758-1.563-1.559a2.267,2.267,0,0,0-3.2,0L6.583,17.42V5.229a.646.646,0,0,1,.646-.646Z" transform="translate(1.166)" fill="#2bb8a6"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="31.908" height="29.134" viewBox="0 0 31.908 29.134">
<g id="Group_8579" data-name="Group 8579" transform="translate(0 -100)">
<path id="Path_4556" data-name="Path 4556" d="M88.972,88.108h-4.4l-2.539-2.775H72.324v4.162H68.162v4.162H64V107.53a2.783,2.783,0,0,0,2.775,2.775h22.2a2.783,2.783,0,0,0,2.775-2.775V90.882A2.783,2.783,0,0,0,88.972,88.108Zm-11.1,18.035a6.937,6.937,0,1,1,6.937-6.937A6.939,6.939,0,0,1,77.873,106.143Z" transform="translate(-59.838 18.829)" fill="#2bb8a6"/>
<path id="Path_4557" data-name="Path 4557" d="M4.162,32.432H6.937V28.27H11.1V25.495H6.937V21.333H4.162v4.162H0V28.27H4.162Z" transform="translate(0 78.667)" fill="#2bb8a6"/>
<path id="Path_4558" data-name="Path 4558" d="M213.505,230.4a4.435,4.435,0,0,0-4.439,4.439h0a4.439,4.439,0,1,0,4.439-4.439Z" transform="translate(-195.47 -116.804)" fill="#2bb8a6"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 910 B

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="37.982" height="37.982" viewBox="0 0 37.982 37.982">
<path id="Path_4586" data-name="Path 4586" d="M18.991,0A18.991,18.991,0,1,0,37.982,18.991,19.013,19.013,0,0,0,18.991,0ZM9.5,17.8A3.555,3.555,0,0,1,11.2,14.777L8.75,12.793a1.187,1.187,0,1,1,1.491-1.847l5.052,4.088A3.561,3.561,0,1,1,9.5,17.8Zm16.29,12.688a1.188,1.188,0,0,1-1.678.04,7.42,7.42,0,0,0-10.234,0,1.187,1.187,0,1,1-1.636-1.721,9.787,9.787,0,0,1,13.5,0A1.187,1.187,0,0,1,25.785,30.492Zm3.447-17.7-2.455,1.985a3.549,3.549,0,1,1-4.09.256l5.054-4.088a1.187,1.187,0,1,1,1.491,1.847Z" fill="#EFEFF0"/>
</svg>

After

Width:  |  Height:  |  Size: 614 B

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="37.982" height="37.982" viewBox="0 0 37.982 37.982">
<path id="Path_4587" data-name="Path 4587" d="M18.991,0A18.991,18.991,0,1,0,37.982,18.991,19.013,19.013,0,0,0,18.991,0ZM9.5,14.243a2.374,2.374,0,1,1,2.374,2.374A2.378,2.378,0,0,1,9.5,14.243ZM27.383,28.895a1.192,1.192,0,0,1-1.681,0,9.72,9.72,0,0,0-13.429,0,1.187,1.187,0,0,1-1.678-1.678,11.869,11.869,0,0,1,16.788,0A1.188,1.188,0,0,1,27.383,28.895Zm-1.27-12.278a2.374,2.374,0,1,1,2.374-2.374A2.378,2.378,0,0,1,26.113,16.617Z" fill="#EFEFF0"/>
</svg>

After

Width:  |  Height:  |  Size: 551 B

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="37.982" height="37.982" viewBox="0 0 37.982 37.982">
<path id="Path_4588" data-name="Path 4588" d="M18.991,0A18.991,18.991,0,1,0,37.982,18.991,19.013,19.013,0,0,0,18.991,0ZM9.5,14.243a2.374,2.374,0,1,1,2.374,2.374A2.378,2.378,0,0,1,9.5,14.243Zm15.43,14.243H13.056a1.187,1.187,0,1,1,0-2.374H24.926a1.187,1.187,0,1,1,0,2.374Zm1.187-11.869a2.374,2.374,0,1,1,2.374-2.374A2.378,2.378,0,0,1,26.113,16.617Z" fill="#EFEFF0"/>
</svg>

After

Width:  |  Height:  |  Size: 474 B

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="37.981" height="37.982" viewBox="0 0 37.981 37.982">
<path id="Path_4589" data-name="Path 4589" d="M18.991,0A18.991,18.991,0,1,0,37.981,18.991,19.013,19.013,0,0,0,18.991,0ZM8.308,14.243A3.561,3.561,0,1,1,11.869,17.8,3.565,3.565,0,0,1,8.308,14.243ZM27.382,27.382a11.866,11.866,0,0,1-16.785,0A1.187,1.187,0,0,1,12.275,25.7a9.72,9.72,0,0,0,13.429,0,1.187,1.187,0,0,1,1.678,1.678ZM26.112,17.8a3.561,3.561,0,1,1,3.561-3.561A3.565,3.565,0,0,1,26.112,17.8Z" fill="#EFEFF0"/>
</svg>

After

Width:  |  Height:  |  Size: 524 B

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="37.982" height="37.982" viewBox="0 0 37.982 37.982">
<path id="Path_4590" data-name="Path 4590" d="M18.991,0A18.991,18.991,0,1,0,37.982,18.991,19.013,19.013,0,0,0,18.991,0Zm7.122,11.869a2.374,2.374,0,1,1-2.374,2.374A2.378,2.378,0,0,1,26.113,11.869Zm-14.243,0A2.374,2.374,0,1,1,9.5,14.243,2.378,2.378,0,0,1,11.869,11.869Zm7.122,21.365a14.275,14.275,0,0,1-13.7-10.36,1.188,1.188,0,0,1,1.142-1.51H31.549a1.183,1.183,0,0,1,.945.47,1.2,1.2,0,0,1,.2,1.04A14.275,14.275,0,0,1,18.991,33.234Z" fill="#EFEFF0"/>
</svg>

After

Width:  |  Height:  |  Size: 558 B

@ -876,5 +876,6 @@
"endDate": "تاريخ الانتهاء",
"walkin": "زيارة بدون موعد",
"laserClinic": "عيادة الليزر",
"continueString": "يكمل"
"continueString": "يكمل",
"covid_info": "تجري مستشفيات د. سليمان الحبيب فحص فيروس كورونا المستجد وتصدر شهادات السفر على مدار الساعة، طوال أيام الأسبوع، وبسرعة ودقة عالية. يمكن للراغبين في الاستفادة من هذه الخدمة زيارة أحد فروع مستشفيات د. سليمان الحبيب وإجراء فحص كورونا خلال بضع دقائق والحصول على النتائج خلال عدة ساعات خدمة فحص فيروس كورونا Covid 19 بتقنية PCR للكشف عن الفيروس وفقاً لأعلى المعايير العالمية وبأحدث أجهزة RT-PCR عالية الدقة (GeneXpert الأمريكي وغيره)، وهي طرق معتمدة من قبل هيئة الغذاء والدواء وكذلك من قبل المركز السعودي للوقاية من الأمراض المُعدية"
}

@ -872,6 +872,6 @@
"searchClinic": "Search Clinic",
"walkin": "Walk In",
"continueString": "Continue",
"laserClinic": "Laser Clinic"
"laserClinic": "Laser Clinic",
"covid_info" :"Dr. Sulaiman Al Habib hospitals are conducting a test for the emerging corona virus and issuing travel certificates 24/7 in a short time and with high accuracy. Those wishing to benefit from this service can visit one of Dr. Sulaiman Al Habib branches to conduct a corona test within few minutes, and obtain the result within several hours. Corona Virus Covid 19 testing service with PCR technology to detect the virus according to the highest international standards and with the latest high-precision RT-PCR devices (American GeneXpert and others), That is approved by the Food and Drug Authority as well as by the Saudi Center for Infectious Diseases Prevention."
}

@ -14,8 +14,8 @@ var PACKAGES_ORDERS = '/api/orders';
var PACKAGES_ORDER_HISTORY = '/api/orders/items';
var PACKAGES_TAMARA_OPT = '/api/orders/paymentoptions/tamara';
// var BASE_URL = 'http://10.50.100.198:2018/';
var BASE_URL = 'https://uat.hmgwebservices.com/';
// var BASE_URL = 'https://hmgwebservices.com/';
// var BASE_URL = 'https://uat.hmgwebservices.com/';
var BASE_URL = 'https://hmgwebservices.com/';
// var BASE_URL = 'http://10.201.204.103/';
// var BASE_URL = 'https://orash.cloudsolutions.com.sa/';
// var BASE_URL = 'https://vidauat.cloudsolutions.com.sa/';
@ -207,7 +207,7 @@ var GET_APPOINTMENT_DETAILS_BY_NO = 'Services/MobileNotifications.svc/REST/GetAp
var NEW_RATE_APPOINTMENT_URL = "Services/Doctors.svc/REST/AppointmentsRating_InsertAppointmentRate";
var NEW_RATE_DOCTOR_URL = "Services/Doctors.svc/REST/DoctorsRating_InsertDoctorRate";
var GET_QR_PARKING = 'Services/SWP.svc/REST/GetQRParkingByID';
//var GET_QR_PARKING = 'Services/SWP.svc/REST/GetQRParkingByID';
//URL to get clinic list
var GET_CLINICS_LIST_URL = "Services/lists.svc/REST/GetClinicCentralized";
@ -828,6 +828,7 @@ class ApiConsts {
static final String getTermsConditions = 'Services/Patients.svc/Rest/GetUserTermsAndConditions';
static final String getMonthlyReports = 'Services/Patients.svc/Rest/UpdatePateintHealthSummaryReport';
static final String updatePatientEmail = 'Services/Patients.svc/Rest/UpdatePateintEmail';
static final String getQrParkingDetails = 'Services/SWP.svc/REST/GetQRParkingByID';
// Ancillary Order Apis
static final String getOnlineAncillaryOrderList = 'Services/Doctors.svc/REST/GetOnlineAncillaryOrderList';

@ -262,6 +262,8 @@ class AppAssets {
static const String pregnancyDayAccordion = '$svgBasePath/pregnancy_test_day_accordion.svg';
static const String pregnancyDueDateAccordion = '$svgBasePath/due_date_accordion.svg';
static const String covid19icon = '$svgBasePath/covid_19.svg';
// PNGS //
static const String hmgLogo = '$pngBasePath/hmg_logo.png';
static const String liveCareService = '$pngBasePath/livecare_service.png';
@ -287,6 +289,9 @@ class AppAssets {
static const String fullBodyFront = '$pngBasePath/full_body_front.png';
static const String fullBodyBack = '$pngBasePath/full_body_back.png';
}
class AppAnimations {
@ -307,5 +312,7 @@ class AppAnimations {
static const String ambulance = '$lottieBasePath/ambulance.json';
static const String ambulanceAlert = '$lottieBasePath/ambulance_alert.json';
static const String rrtAmbulance = '$lottieBasePath/rrt_ambulance.json';
}

@ -4,10 +4,8 @@ import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:gms_check/gms_check.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/HMCProjectListModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/PrivilegeModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/ProjectDetailListModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/VidaPlusProjectListModel.dart';
import 'package:hmg_patient_app_new/features/authentication/models/request_models/send_activation_request_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_user_staus_nhic_response_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart';
@ -45,7 +43,7 @@ class AppState {
bool isChildLoggedIn = false;
bool isGMSAvailable = true;
bool isAndroid = true;
bool isRatedVisible =false;
void setAuthenticatedUser(AuthenticatedUser? authenticatedUser, {bool isFamily = false}) {
if (isFamily) {
_authenticatedChildUser = authenticatedUser;
@ -172,4 +170,8 @@ class AppState {
userLong = 0.0;
userLong = 0.0;
}
setRatedVisible(bool value) {
isRatedVisible = value;
}
}

@ -41,6 +41,7 @@ import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
import 'package:hmg_patient_app_new/features/qr_parking/qr_parking_repo.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_provider.dart';
@ -48,7 +49,7 @@ import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_r
import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_view_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_repo.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_view_model.dart';
// import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_view_model.dart';
import 'package:hmg_patient_app_new/presentation/monthly_reports/monthly_reports_page.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
@ -63,6 +64,7 @@ import 'package:local_auth/local_auth.dart';
import 'package:logger/web.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../features/qr_parking/qr_parking_view_model.dart';
import '../presentation/health_calculators_and_converts/health_calculator_view_model.dart';
import '../features/active_prescriptions/active_prescriptions_repo.dart';
@ -140,6 +142,15 @@ class AppDependencies {
getIt.registerFactory<TermsConditionsViewModel>(() => TermsConditionsViewModel(termsConditionsRepo: getIt<TermsConditionsRepo>(), errorHandlerService: getIt<ErrorHandlerService>(),
),);
getIt.registerLazySingleton<MonthlyReportsRepo>(() => MonthlyReportsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<QrParkingRepo>(() => QrParkingRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerFactory<QrParkingViewModel>(
() => QrParkingViewModel(
qrParkingRepo: getIt<QrParkingRepo>(),
errorHandlerService: getIt<ErrorHandlerService>(),
cacheService: getIt<CacheService>(),
),
);
// ViewModels
// Global/shared VMs LazySingleton
@ -245,6 +256,14 @@ class AppDependencies {
activePrescriptionsRepo: getIt()
),
);
getIt.registerFactory<QrParkingViewModel>(
() => QrParkingViewModel(
qrParkingRepo: getIt<QrParkingRepo>(),
errorHandlerService: getIt<ErrorHandlerService>(),
cacheService: getIt<CacheService>(),
),
);
// Screen-specific VMs Factory
// getIt.registerFactory<BookAppointmentsViewModel>(

@ -1,7 +1,5 @@
import 'dart:convert';
import 'package:equatable/equatable.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
class APIException implements Exception {
static const String BAD_REQUEST = 'api_common_bad_request';

@ -1,7 +1,5 @@
import 'dart:math';
import 'package:hmg_patient_app_new/core/cache_consts.dart' show CacheConst;
import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils;
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart' show RegionList, PatientDoctorAppointmentList, DoctorList, PatientDoctorAppointmentListByRegion;
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel;

@ -18,10 +18,8 @@ import 'package:flutter_ios_voip_kit_karmm/flutter_ios_voip_kit.dart';
// import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/utils/local_notifications.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:uuid/uuid.dart';

@ -1,6 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
class SizeConfig {
static double _blockWidth = 0;

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
extension IntExtensions on int {
Widget get height => SizedBox(height: toDouble());

@ -5,10 +5,8 @@ import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/PrivilegeModel.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/authentication/models/request_models/check_activation_code_register_request_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';

@ -27,7 +27,6 @@ import 'package:hmg_patient_app_new/features/authentication/models/resp_models/a
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_activation_code_resp_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_user_staus_nhic_response_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';

@ -66,6 +66,7 @@ class BloodDonationRepoImp implements BloodDonationRepo {
await apiClient.post(
GET_BLOOD_REQUEST,
body: mapDevice,
isAllowAny: true,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},

@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_repo.dart';
import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_list_model.dart';
import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_response_model.dart';
@ -31,7 +32,7 @@ class BloodDonationViewModel extends ChangeNotifier {
List<BloodGroupListModel> genderList = [
BloodGroupListModel(LocaleKeys.malE.tr(), 1),
BloodGroupListModel(LocaleKeys.female.tr(), 2),
BloodGroupListModel("Female".needTranslation.tr(), 2),
];
late CitiesModel selectedCity;

@ -1,4 +1,3 @@
import 'dart:io';
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
@ -26,9 +25,12 @@ abstract class BookAppointmentsRepo {
Future<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getDoctorsList(int clinicID, int projectID, bool isNearest, int doctorId, String doctorName, {isContinueDentalPlan = false});
Future<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getDoctorsListByHealthCal(int calculationID);
Future<Either<Failure, GenericApiModel<DoctorsProfileResponseModel>>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare, {bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare,
{bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel});
@ -83,8 +85,7 @@ abstract class BookAppointmentsRepo {
Future<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getDentalChiefComplaintDoctorsList(int projectID, int chiefComplaintID,
{Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<List<LaserBodyPart>>>> getLaserClinics(int laserCategoryID, int projectID, int languageID,
{Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<List<LaserBodyPart>>>> getLaserClinics(int laserCategoryID, int projectID, int languageID, {Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> checkScannedNFCAndQRCode(String nfcCode, int projectId, {Function(dynamic)? onSuccess, Function(String)? onError});
@ -206,6 +207,45 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
}
}
@override
Future<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getDoctorsListByHealthCal(int calculationID, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {"CalculationID": calculationID};
try {
GenericApiModel<List<DoctorsListResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_DOCTOR_LIST_CALCULATION,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
onError!(error);
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['List_CalculationTable'];
final doctorsList = list.map((item) => DoctorsListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<DoctorsListResponseModel>();
apiResponse = GenericApiModel<List<DoctorsListResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: doctorsList,
);
} 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<DoctorsProfileResponseModel>>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
@ -824,7 +864,8 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
}
@override
Future<Either<Failure, GenericApiModel<List<LaserBodyPart>>>> getLaserClinics(int laserCategoryID, int projectID, int languageID, {Function(dynamic p1)? onSuccess, Function(String p1)? onError}) async {
Future<Either<Failure, GenericApiModel<List<LaserBodyPart>>>> getLaserClinics(int laserCategoryID, int projectID, int languageID,
{Function(dynamic p1)? onSuccess, Function(String p1)? onError}) async {
Map<String, dynamic> mapDevice = {
"LaserCategoryID": laserCategoryID,
"ProjectID": projectID,

@ -46,9 +46,14 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool isDoctorSearchByNameStarted = false;
bool isLiveCareSchedule = false;
bool isGetDocForHealthCal = false;
int? calculationID = 0;
bool isSortByClinic = true;
int initialSlotDuration = 0;
bool isNearestAppointmentSelected = false;
LocationUtils locationUtils;
List<GetClinicsListResponseModel> clinicsList = [];
@ -61,6 +66,11 @@ class BookAppointmentsViewModel extends ChangeNotifier {
List<DoctorsListResponseModel> doctorsList = [];
List<DoctorsListResponseModel> filteredDoctorList = [];
// Grouped doctors lists
List<List<DoctorsListResponseModel>> doctorsListByClinic = [];
List<List<DoctorsListResponseModel>> doctorsListByHospital = [];
List<List<DoctorsListResponseModel>> doctorsListGrouped = [];
List<DoctorsListResponseModel> liveCareDoctorsList = [];
List<PatientDentalPlanEstimationResponseModel> patientDentalPlanEstimationList = [];
@ -143,6 +153,31 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool isBodyPartsLoading = false;
int duration = 0;
setIsSortByClinic(bool value) {
isSortByClinic = value;
doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital;
notifyListeners();
}
// Group doctors by clinic and hospital
void _groupDoctorsList() {
final clinicMap = <String, List<DoctorsListResponseModel>>{};
final hospitalMap = <String, List<DoctorsListResponseModel>>{};
for (var doctor in doctorsList) {
final clinicKey = (doctor.clinicName ?? 'Unknown').trim();
clinicMap.putIfAbsent(clinicKey, () => []).add(doctor);
final hospitalKey = (doctor.projectName ?? 'Unknown').trim();
hospitalMap.putIfAbsent(hospitalKey, () => []).add(doctor);
}
doctorsListByClinic = clinicMap.values.toList();
doctorsListByHospital = hospitalMap.values.toList();
doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital;
}
BookAppointmentsViewModel(
{required this.bookAppointmentsRepo,
required this.errorHandlerService,
@ -187,6 +222,18 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
setIsNearestAppointmentSelected(bool isNearestAppointmentSelected) {
this.isNearestAppointmentSelected = isNearestAppointmentSelected;
if (isNearestAppointmentSelected) {
doctorsList.sort((a, b) => DateUtil.convertStringToDate(a.nearestFreeSlot!).compareTo(DateUtil.convertStringToDate(b.nearestFreeSlot!)));
} else {
doctorsList.sort((a, b) => b.decimalDoctorRate!.compareTo(a.decimalDoctorRate!));
}
notifyListeners();
}
setIsWaitingAppointmentSelected(bool isWaitingAppointmentSelected) {
this.isWaitingAppointmentSelected = isWaitingAppointmentSelected;
notifyListeners();
@ -280,6 +327,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
void onTabChanged(int index) {
calculationID = null;
isGetDocForHealthCal = false;
selectedTabIndex = index;
notifyListeners();
}
@ -319,8 +368,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
Future<void> getLiveCareScheduleClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
liveCareClinicsList.clear();
final result =
await bookAppointmentsRepo.getLiveCareScheduleClinics(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!);
final result = await bookAppointmentsRepo.getLiveCareScheduleClinics(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
@ -342,9 +390,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
Future<void> getLiveCareDoctorsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear();
final result = await bookAppointmentsRepo.getLiveCareDoctorsList(
selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!,
onError: onError);
final result =
await bookAppointmentsRepo.getLiveCareDoctorsList(selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, onError: onError);
result.fold(
(failure) async {
@ -367,11 +414,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
//TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName
Future<void> getDoctorsList(
{int projectID = 0, bool isNearest = true, int doctorId = 0,
String doctorName = "",
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
Future<void> getDoctorsList({int projectID = 0, bool isNearest = true, int doctorId = 0, String doctorName = "", Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear();
projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID;
final result =
@ -391,9 +434,43 @@ class BookAppointmentsViewModel extends ChangeNotifier {
doctorsList = apiResponse.data!;
filteredDoctorList = doctorsList;
isDoctorsListLoading = false;
doctorsList.sort((a, b) => b.decimalDoctorRate!.compareTo(a.decimalDoctorRate!));
initializeFilteredList();
clearSearchFilters();
getFiltersFromDoctorList();
_groupDoctorsList();
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
//TODO: GetDockets & Calculations For Health Calculator
Future<void> getDoctorsListByHealthCal({Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear();
final result = await bookAppointmentsRepo.getDoctorsListByHealthCal(calculationID!);
result.fold(
(failure) async {
isDoctorsListLoading = false;
if (onError != null) onError("No doctors found for the search criteria".needTranslation);
notifyListeners();
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
doctorsList = apiResponse.data!;
setIsSortByClinic(true);
filteredDoctorList = doctorsList;
isDoctorsListLoading = false;
initializeFilteredList();
clearSearchFilters();
getFiltersFromDoctorList();
_groupDoctorsList();
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
@ -404,13 +481,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
Future<void> getMappedDoctors(
{int projectID = 0,
bool isNearest = false,
int doctorId = 0,
String doctorName = "",
isContinueDentalPlan = false,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
{int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
filteredHospitalList = null;
hospitalList = null;
isRegionListLoading = true;
@ -446,8 +517,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
Future<void> getDoctorProfile({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await bookAppointmentsRepo
.getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError);
final result = await bookAppointmentsRepo.getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError);
result.fold(
(failure) async {},
@ -506,8 +576,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
// :
date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString()));
slotsList.add(FreeSlot(date, ['slot']));
docFreeSlots.add(TimeSlot(
isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
docFreeSlots.add(TimeSlot(isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
});
notifyListeners();
@ -526,8 +595,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
final DateFormat dateFormatter = DateFormat('yyyy-MM-dd');
Map<DateTime, List> _eventsParsed;
final result = await bookAppointmentsRepo.getLiveCareDoctorFreeSlots(selectedDoctor.clinicID ?? 0, selectedLiveCareClinic.serviceID ?? 0,
selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare,
final result = await bookAppointmentsRepo.getLiveCareDoctorFreeSlots(
selectedDoctor.clinicID ?? 0, selectedLiveCareClinic.serviceID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare,
onError: onError);
result.fold(
@ -551,8 +620,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
// :
date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString()));
slotsList.add(FreeSlot(date, ['slot']));
docFreeSlots.add(TimeSlot(
isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
docFreeSlots.add(TimeSlot(isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
});
notifyListeners();
@ -564,10 +632,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
);
}
Future<void> cancelAppointment(
{required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
Future<void> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel);
result.fold(
@ -651,15 +716,13 @@ class BookAppointmentsViewModel extends ChangeNotifier {
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
navigationService.pop();
Future.delayed(Duration(milliseconds: 50)).then((value) async {});
LoadingUtils.showFullScreenLoader(
barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
await insertSpecificAppointment(
onError: (err) {},
onSuccess: (apiResp) async {
LoadingUtils.hideFullScreenLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async {
LoadingUtils.showFullScreenLoader(
barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
await Future.delayed(Duration(milliseconds: 4000)).then((value) {
LoadingUtils.hideFullScreenLoader();
Navigator.pushAndRemoveUntil(
@ -749,15 +812,13 @@ class BookAppointmentsViewModel extends ChangeNotifier {
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
navigationService.pop();
Future.delayed(Duration(milliseconds: 50)).then((value) async {});
LoadingUtils.showFullScreenLoader(
barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
await insertSpecificAppointment(
onError: (err) {},
onSuccess: (apiResp) async {
LoadingUtils.hideFullScreenLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async {
LoadingUtils.showFullScreenLoader(
barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
await Future.delayed(Duration(milliseconds: 4000)).then((value) {
LoadingUtils.hideFullScreenLoader();
Navigator.pushAndRemoveUntil(
@ -831,9 +892,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} else {
filteredHospitalList = RegionList();
var list = isHMG
? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList
: hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList;
var list = isHMG ? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList : hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList;
if (list != null && list.isEmpty) {
notifyListeners();
@ -916,8 +975,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
void setSelections(List<String>? selectedFacilityForFilters, List<String>? selectedRegionForFilters, String? selectedClinicForFilters,
PatientDoctorAppointmentList? selectedHospitalForFilters, bool applyFilters) {
void setSelections(
List<String>? selectedFacilityForFilters, List<String>? selectedRegionForFilters, String? selectedClinicForFilters, PatientDoctorAppointmentList? selectedHospitalForFilters, bool applyFilters) {
this.selectedFacilityForFilters = selectedFacilityForFilters;
this.selectedClinicForFilters = selectedClinicForFilters;
this.selectedHospitalForFilters = selectedHospitalForFilters;
@ -985,15 +1044,11 @@ class BookAppointmentsViewModel extends ChangeNotifier {
List<DoctorsListResponseModel> getDoctorListAsPerSelection() {
if (!applyFilters) return doctorsList;
if ((selectedRegionForFilters?.isEmpty == true) &&
(selectedFacilityForFilters?.isEmpty == true) &&
selectedClinicForFilters == null &&
selectedHospitalForFilters == null) {
if ((selectedRegionForFilters?.isEmpty == true) && (selectedFacilityForFilters?.isEmpty == true) && selectedClinicForFilters == null && selectedHospitalForFilters == null) {
return doctorsList;
}
var list = doctorsList.where((element) {
var isInSelectedRegion =
(selectedRegionForFilters?.isEmpty == true) ? true : selectedRegionForFilters?.any((region) => region == element.getRegionName(isArabic()));
var isInSelectedRegion = (selectedRegionForFilters?.isEmpty == true) ? true : selectedRegionForFilters?.any((region) => region == element.getRegionName(isArabic()));
var shouldApplyFacilityFilter = (selectedFacilityForFilters?.isEmpty == true) ? false : true;
var isHMC = (selectedFacilityForFilters?.isEmpty == true) ? true : selectedFacilityForFilters?.any((item) => item.contains("hmc"));
var isInSelectedClinic = (selectedClinicForFilters == null) ? true : selectedClinicForFilters == element.clinicName;
@ -1044,8 +1099,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
dentalChiefComplaintsList.clear();
notifyListeners();
int patientID = _appState.isAuthenticated ? _appState.getAuthenticatedUser()!.patientId ?? -1 : -1;
final result = await bookAppointmentsRepo.getDentalChiefComplaintsList(
patientID: patientID, projectID: int.parse(currentlySelectedHospitalFromRegionFlow ?? "0"), clinicID: 17);
final result = await bookAppointmentsRepo.getDentalChiefComplaintsList(patientID: patientID, projectID: int.parse(currentlySelectedHospitalFromRegionFlow ?? "0"), clinicID: 17);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
@ -1084,6 +1138,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
// initializeFilteredList();
// clearSearchFilters();
// getFiltersFromDoctorList();
_groupDoctorsList();
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);

@ -65,75 +65,77 @@ class DoctorsListResponseModel {
String? regionID;
String? projectBottomName;
String? projectTopName;
int? calcID;
DoctorsListResponseModel(
{this.clinicID,
this.clinicName,
this.clinicNameN,
this.doctorTitle,
this.iD,
this.name,
this.projectID,
this.projectName,
this.actualDoctorRate,
this.clinicRoomNo,
this.date,
this.dayName,
this.decimalDoctorRate,
this.doctorAvailability,
this.doctorID,
this.doctorImageURL,
this.doctorMobileNumber,
this.doctorProfile,
this.doctorProfileInfo,
this.doctorRate,
this.doctorStarsRate,
this.employmentType,
this.gender,
this.genderDescription,
this.hISRegionId,
this.isActive,
this.isAllowWaitList,
this.isAppointmentAllowed,
this.isDoctorAllowVedioCall,
this.isDoctorDummy,
this.isDoctorHasPrePostImages,
this.isHMC,
this.isHmg,
this.isLiveCare,
this.latitude,
this.longitude,
this.nationalityFlagURL,
this.nationalityID,
this.nationalityName,
this.nearestFreeSlot,
this.noOfFreeSlotsAvailable,
this.noOfPatientsRate,
this.originalClinicID,
this.personRate,
this.projectDistanceInKiloMeters,
this.projectNameBottom,
this.projectNameTop,
this.qR,
this.qRString,
this.rateNumber,
this.regionName,
this.regionNameN,
this.serviceID,
this.setupID,
this.speciality,
this.specialityN,
this.transactionType,
this.virtualEmploymentType,
this.workingHours,
this.vida3Id,
this.region,
this.regionArabic,
this.regionEnglish,
this.regionID,
this.projectBottomName,
this.projectTopName,});
DoctorsListResponseModel({
this.clinicID,
this.clinicName,
this.clinicNameN,
this.doctorTitle,
this.iD,
this.name,
this.projectID,
this.projectName,
this.actualDoctorRate,
this.clinicRoomNo,
this.date,
this.dayName,
this.decimalDoctorRate,
this.doctorAvailability,
this.doctorID,
this.doctorImageURL,
this.doctorMobileNumber,
this.doctorProfile,
this.doctorProfileInfo,
this.doctorRate,
this.doctorStarsRate,
this.employmentType,
this.gender,
this.genderDescription,
this.hISRegionId,
this.isActive,
this.isAllowWaitList,
this.isAppointmentAllowed,
this.isDoctorAllowVedioCall,
this.isDoctorDummy,
this.isDoctorHasPrePostImages,
this.isHMC,
this.isHmg,
this.isLiveCare,
this.latitude,
this.longitude,
this.nationalityFlagURL,
this.nationalityID,
this.nationalityName,
this.nearestFreeSlot,
this.noOfFreeSlotsAvailable,
this.noOfPatientsRate,
this.originalClinicID,
this.personRate,
this.projectDistanceInKiloMeters,
this.projectNameBottom,
this.projectNameTop,
this.qR,
this.qRString,
this.rateNumber,
this.regionName,
this.regionNameN,
this.serviceID,
this.setupID,
this.speciality,
this.specialityN,
this.transactionType,
this.virtualEmploymentType,
this.workingHours,
this.vida3Id,
this.region,
this.regionArabic,
this.regionEnglish,
this.regionID,
this.projectBottomName,
this.projectTopName,
this.calcID,
});
DoctorsListResponseModel.fromJson(Map<String, dynamic> json) {
clinicID = json['ClinicID'];
@ -141,7 +143,7 @@ class DoctorsListResponseModel {
clinicNameN = json['ClinicNameN'];
doctorTitle = json['DoctorTitle'];
iD = json['ID'];
name = json['Name'];
name = json['Name'] ?? json["DoctorName"];
projectID = json['ProjectID'];
projectName = json['ProjectName'];
actualDoctorRate = json['ActualDoctorRate'];
@ -174,7 +176,7 @@ class DoctorsListResponseModel {
longitude = json['Longitude'];
nationalityFlagURL = json['NationalityFlagURL'];
nationalityID = json['NationalityID'];
nationalityName = json['NationalityName'];
nationalityName = json['NationalityName'] ?? json["Nationality"];
nearestFreeSlot = json['NearestFreeSlot'];
noOfFreeSlotsAvailable = json['NoOfFreeSlotsAvailable'];
noOfPatientsRate = json['NoOfPatientsRate'];
@ -200,6 +202,7 @@ class DoctorsListResponseModel {
regionEnglish = json['RegionName'];
projectBottomName = json['ProjectNameBottom'];
projectTopName = json['ProjectNameTop'];
calcID = json["CalcID"];
}
Map<String, dynamic> toJson() {
@ -264,6 +267,7 @@ class DoctorsListResponseModel {
data['VirtualEmploymentType'] = this.virtualEmploymentType;
data['WorkingHours'] = this.workingHours;
data['vida3Id'] = this.vida3Id;
data['CalcID'] = this.calcID;
return data;
}
@ -273,7 +277,8 @@ class DoctorsListResponseModel {
}
return regionEnglish;
}
String getProjectCompleteName(){
String getProjectCompleteName() {
return "${this.projectTopName} ${this.projectBottomName}";
}

@ -14,7 +14,6 @@ import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:provider/provider.dart';
import 'models/req_models/create_e_referral_model.dart';
import 'models/req_models/send_activation_code_ereferral_req_model.dart';

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
@ -18,6 +20,7 @@ import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'models/req_models/check_activation_e_referral_req_model.dart';
import 'models/resq_models/relationship_type_resp_mode.dart';
import 'models/ui_models/covid_questionnare_model.dart';
class HmgServicesViewModel extends ChangeNotifier {
final HmgServicesRepo hmgServicesRepo;
@ -60,6 +63,8 @@ class HmgServicesViewModel extends ChangeNotifier {
Future<void> getOrdersList() async {}
// HHC multiple services selection
List<GetCMCServicesResponseModel> selectedHhcServices = [];
@ -766,4 +771,16 @@ class HmgServicesViewModel extends ChangeNotifier {
},
);
}
List<CovidQuestionnaireModel> getQuestionsFromJson() {
final String questionsJson = '''[ { "id": 1, "questionEN": "Is the test intended for travel?", "questionAR": "هل تجري التحليل بغرض السفر؟", "ans": 2 }, { "id": 2, "questionEN": "Coming from outside KSA within last 2 weeks?", "questionAR": "هل قدمت من خارج المملكة خلال الأسبوعين الماضيين؟", "ans": 2 }, { "id": 3, "questionEN": "Do you currently have fever?", "questionAR": "هل تعاني حاليا من حرارة؟", "ans": 2 }, { "id": 4, "questionEN": "Did you have fever in last 2 weeks?", "questionAR": "هل عانيت من حرارة في الأسبوعين الماضيين؟", "ans": 2 }, { "id": 5, "questionEN": "Do you have a sore throat?", "questionAR": "هل لديك التهاب في الحلق؟", "ans": 2 }, { "id": 6, "questionEN": "Do you have a runny nose?", "questionAR": "هل لديك سيلان بالأنف؟" }, { "id": 7, "questionEN": "Do you have a cough?", "questionAR": "هل لديك سعال؟", "ans": 2 }, { "id": 8, "questionEN": "Do you have shortness of breath?", "questionAR": "هل تعاني من ضيق في التنفس؟", "ans": 2 }, { "id": 9, "questionEN": "Do you have nausea?", "questionAR": "هل تعاني من غثيان؟", "ans": 2 }, { "id": 10, "questionEN": "Do you have vomiting?", "questionAR": "هل تعاني من القيء؟", "ans": 2 }, { "id": 11, "questionEN": "Do you have a headache?", "questionAR": "هل تعاني من صداع في الرأس؟", "ans": 2 }, { "id": 12, "questionEN": "Do you have muscle pain?", "questionAR": "هل تعانين من آلام عضلية؟", "ans": 2 }, { "id": 13, "questionEN": "Do you have joint pain?", "questionAR": "هل تعاني من آلام المفاصل؟", "ans": 2 }, { "id": 14, "questionEN": "Do you have diarrhea?", "questionAR": "هل لديك اسهال؟", "ans": 2 } ]''';
try {
final parsed = json.decode(questionsJson) as List<dynamic>;
return parsed
.map((e) => CovidQuestionnaireModel.fromJson(Map<String, dynamic>.from(e)))
.toList();
} catch (_) {
return <CovidQuestionnaireModel>[];
}
}
}

@ -0,0 +1,33 @@
import 'dart:convert';
class CovidQuestionnaireModel {
int? id;
String? questionEn;
String? questionAr;
int? ans;
CovidQuestionnaireModel({
this.id,
this.questionEn,
this.questionAr,
this.ans,
});
factory CovidQuestionnaireModel.fromRawJson(String str) => CovidQuestionnaireModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory CovidQuestionnaireModel.fromJson(Map<String, dynamic> json) => CovidQuestionnaireModel(
id: json["id"],
questionEn: json["questionEN"],
questionAr: json["questionAR"],
ans: json["ans"],
);
Map<String, dynamic> toJson() => {
"id": id,
"questionEN": questionEn,
"questionAR": questionAr,
"ans": ans,
};
}

@ -1,5 +1,4 @@
// models/referral_models.dart
import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart';

@ -5,7 +5,6 @@ import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patien
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_card_history.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart';
import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_update_response_model.dart';
import 'package:hmg_patient_app_new/features/lab/lab_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class InsuranceViewModel extends ChangeNotifier {

@ -1,6 +1,5 @@
import 'dart:collection';
import 'dart:core';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
@ -18,7 +17,6 @@ import 'package:hmg_patient_app_new/theme/colors.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:intl/intl.dart' show DateFormat;
import 'package:logger/logger.dart';
class LabViewModel extends ChangeNotifier {
bool isLabOrdersLoading = false;
@ -82,6 +80,12 @@ class LabViewModel extends ChangeNotifier {
double minY = double.infinity;
double maxX = double.infinity;
// --- New grouping fields ---
bool isSortByClinic = true;
List<List<PatientLabOrdersResponseModel>> patientLabOrdersByClinic = [];
List<List<PatientLabOrdersResponseModel>> patientLabOrdersByHospital = [];
List<List<PatientLabOrdersResponseModel>> patientLabOrdersViewList = [];
LabViewModel(
{required this.labRepo,
required this.errorHandlerService,
@ -94,6 +98,9 @@ class LabViewModel extends ChangeNotifier {
labOrderTests.clear();
isLabOrdersLoading = true;
isLabResultsLoading = true;
patientLabOrdersByClinic.clear();
patientLabOrdersByHospital.clear();
patientLabOrdersViewList.clear();
getPatientLabOrders();
// }
notifyListeners();
@ -104,6 +111,12 @@ class LabViewModel extends ChangeNotifier {
notifyListeners();
}
void setIsSortByClinic(bool value) {
isSortByClinic = value;
patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
notifyListeners();
}
Future<void> getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
// if (!isLabNeedToLoad) return;
@ -112,6 +125,9 @@ class LabViewModel extends ChangeNotifier {
uniqueTests.clear();
labOrderTests.clear();
uniqueTests = {};
patientLabOrdersByClinic.clear();
patientLabOrdersByHospital.clear();
patientLabOrdersViewList.clear();
notifyListeners();
final result = await labRepo.getPatientLabOrders();
@ -122,7 +138,7 @@ class LabViewModel extends ChangeNotifier {
isLabResultsLoading = false;
notifyListeners();
},
// => await errorHandlerService.handleError(failure: failure),
// => await errorHandler_service.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
@ -132,6 +148,23 @@ class LabViewModel extends ChangeNotifier {
tempLabOrdersList = apiResponse.data!;
isLabOrdersLoading = false;
isLabResultsLoading = false;
// --- Build groups by clinic and by hospital (projectName) ---
final clinicMap = <String, List<PatientLabOrdersResponseModel>>{};
final hospitalMap = <String, List<PatientLabOrdersResponseModel>>{};
for (var order in patientLabOrders) {
final clinicKey = (order.clinicDescription ?? 'Unknown').trim();
clinicMap.putIfAbsent(clinicKey, () => []).add(order);
final hospitalKey = (order.projectName ?? order.projectID ?? 'Unknown').toString().trim();
hospitalMap.putIfAbsent(hospitalKey, () => []).add(order);
}
patientLabOrdersByClinic = clinicMap.values.toList();
patientLabOrdersByHospital = hospitalMap.values.toList();
patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
filterSuggestions();
getUniqueTestDescription();
isLabNeedToLoad = false;
@ -206,7 +239,6 @@ class LabViewModel extends ChangeNotifier {
createdOn: item.createdOn,
model: item))
};
var sortedResult = SplayTreeSet<TestDetails>.from(uniqueTests, (a, b) => a.description?[0].toUpperCase().compareTo(b.description?[0] ?? "") ?? -1);
uniqueTestsList = uniqueTests.toList();
uniqueTestsList.sort((a, b) {
return a.description!.toLowerCase().compareTo(b.description!.toLowerCase());
@ -291,7 +323,7 @@ class LabViewModel extends ChangeNotifier {
PatientLabOrdersResponseModel laborder) async {
isLabResultByHospitalLoading = true;
notifyListeners();
mainLabResultsByHospitals.clear;
mainLabResultsByHospitals.clear();
final result = await labRepo.getPatientLabResultsByHospitals(laborder,
Utils.isVidaPlusProject(int.parse(laborder.projectID ?? "0")));
@ -334,8 +366,7 @@ class LabViewModel extends ChangeNotifier {
LoaderBottomSheet.hideLoader();
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
var sortedResponse = sortByFlagAndValue(apiResponse.data!);
var recentThree = sort(sortedResponse);
var recentThree = sort(apiResponse.data!);
mainLabResults = recentThree;
@ -617,19 +648,6 @@ class LabViewModel extends ChangeNotifier {
}
List<LabResult> sort(List<LabResult> original) {
DateTime? parseVerifiedDate(String? raw) {
if (raw == null) return null;
final regex = RegExp(r'\/Date\((\d+)\)\/');
final match = regex.firstMatch(raw);
if (match != null) {
final millis = int.tryParse(match.group(1)!);
if (millis != null) {
return DateTime.fromMillisecondsSinceEpoch(millis);
}
}
return null;
}
final copy = List<LabResult>.from(original);
copy.sort((a, b) {
@ -649,8 +667,8 @@ class LabViewModel extends ChangeNotifier {
final copy = List<DataPoint>.from(original);
copy.sort((a, b) {
final aDate =a.time;
final bDate = a.time;
final aDate = a.time;
final bDate = b.time;
final now = DateTime.now();
if (aDate == now && bDate == now) return 0;
if (aDate == now) return 1;

@ -37,7 +37,9 @@ class LocationViewModel extends ChangeNotifier {
}
GMSMapServices.CameraPosition getGMSLocation() {
return GMSMapServices.CameraPosition(target: GMSMapServices.LatLng(getIt<AppState>().userLat, getIt<AppState>().userLong), zoom: 18);
return GMSMapServices.CameraPosition(
target: GMSMapServices.LatLng(getIt<AppState>().userLat != 0.0 ? getIt<AppState>().userLat : 24.7248316, getIt<AppState>().userLong != 0.0 ? getIt<AppState>().userLong : 46.4928828),
zoom: 18);
}
void placeValueInController() async {

@ -3,7 +3,6 @@ import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_allergies_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart';

@ -1,4 +1,3 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
@ -36,6 +35,12 @@ class MedicalFileViewModel extends ChangeNotifier {
List<PatientVaccineResponseModel> patientVaccineList = [];
List<PatientSickLeavesResponseModel> patientSickLeaveList = [];
List<SickLeaveList> patientSickLeavesByClinic = [];
List<SickLeaveList> patientSickLeavesByHospital = [];
List<SickLeaveList> patientSickLeavesViewList = [];
bool isSickLeavesSortByClinic = true;
List<GetAllergiesResponseModel> patientAllergiesList = [];
List<PatientMedicalReportResponseModel> patientMedicalReportList = [];
@ -44,6 +49,12 @@ class MedicalFileViewModel extends ChangeNotifier {
List<PatientMedicalReportResponseModel> patientMedicalReportReadyList = [];
List<PatientMedicalReportResponseModel> patientMedicalReportCancelledList = [];
List<MedicalReportList> patientMedicalReportsByClinic = [];
List<MedicalReportList> patientMedicalReportsByHospital = [];
List<MedicalReportList> patientMedicalReportsViewList = [];
bool isMedicalReportsSortByClinic = true;
List<PatientAppointmentHistoryResponseModel> patientMedicalReportAppointmentHistoryList = [];
PatientAppointmentHistoryResponseModel? patientMedicalReportSelectedAppointment;
@ -90,6 +101,49 @@ class MedicalFileViewModel extends ChangeNotifier {
} else if (index == 2) {
patientMedicalReportList = patientMedicalReportCancelledList;
}
// Re-group by clinic/hospital for the selected tab
_groupMedicalReportsByClinicAndHospital();
notifyListeners();
}
void _groupMedicalReportsByClinicAndHospital() {
patientMedicalReportsByClinic.clear();
patientMedicalReportsByHospital.clear();
for (var element in patientMedicalReportList) {
// Group by clinic
List<MedicalReportList> reportsByClinic = patientMedicalReportsByClinic.where((elementClinic) => elementClinic.filterName == element.clinicDescription).toList();
if (reportsByClinic.isNotEmpty) {
patientMedicalReportsByClinic[patientMedicalReportsByClinic.indexOf(reportsByClinic[0])].medicalReportsList!.add(element);
} else {
patientMedicalReportsByClinic.add(MedicalReportList(filterName: element.clinicDescription, medicalReport: element));
}
// Group by hospital
List<MedicalReportList> reportsByHospital = patientMedicalReportsByHospital.where((elementHospital) => elementHospital.filterName == element.projectName).toList();
if (reportsByHospital.isNotEmpty) {
patientMedicalReportsByHospital[patientMedicalReportsByHospital.indexOf(reportsByHospital[0])].medicalReportsList!.add(element);
} else {
patientMedicalReportsByHospital.add(MedicalReportList(filterName: element.projectName, medicalReport: element));
}
}
if (isMedicalReportsSortByClinic) {
patientMedicalReportsViewList = patientMedicalReportsByClinic;
} else {
patientMedicalReportsViewList = patientMedicalReportsByHospital;
}
}
setIsMedicalReportsSortByClinic(bool value) {
isMedicalReportsSortByClinic = value;
if (isMedicalReportsSortByClinic) {
patientMedicalReportsViewList = patientMedicalReportsByClinic;
} else {
patientMedicalReportsViewList = patientMedicalReportsByHospital;
}
notifyListeners();
}
@ -110,17 +164,35 @@ class MedicalFileViewModel extends ChangeNotifier {
setIsPatientSickLeaveListLoading(bool val) {
if (val) {
patientSickLeaveList.clear();
patientSickLeavesByClinic.clear();
patientSickLeavesByHospital.clear();
patientSickLeavesViewList.clear();
patientSickLeavePDFBase64 = "";
isSickLeavesSortByClinic = true;
}
isPatientSickLeaveListLoading = val;
notifyListeners();
}
setIsSickLeavesSortByClinic(bool value) {
isSickLeavesSortByClinic = value;
if (isSickLeavesSortByClinic) {
patientSickLeavesViewList = patientSickLeavesByClinic;
} else {
patientSickLeavesViewList = patientSickLeavesByHospital;
}
notifyListeners();
}
setIsPatientMedicalReportsLoading(bool val) {
if (val) {
onMedicalReportTabChange(0);
patientMedicalReportList.clear();
patientMedicalReportsByClinic.clear();
patientMedicalReportsByHospital.clear();
patientMedicalReportsViewList.clear();
patientMedicalReportPDFBase64 = "";
isMedicalReportsSortByClinic = true;
}
isPatientMedicalReportsListLoading = val;
notifyListeners();
@ -214,6 +286,29 @@ class MedicalFileViewModel extends ChangeNotifier {
} else if (apiResponse.messageStatus == 1) {
patientSickLeaveList = apiResponse.data!;
isPatientSickLeaveListLoading = false;
// Group by clinic and hospital
for (var element in patientSickLeaveList) {
// Group by clinic
List<SickLeaveList> sickLeavesByClinic = patientSickLeavesByClinic.where((elementClinic) => elementClinic.filterName == element.clinicName).toList();
if (sickLeavesByClinic.isNotEmpty) {
patientSickLeavesByClinic[patientSickLeavesByClinic.indexOf(sickLeavesByClinic[0])].sickLeavesList!.add(element);
} else {
patientSickLeavesByClinic.add(SickLeaveList(filterName: element.clinicName, sickLeaves: element));
}
// Group by hospital
List<SickLeaveList> sickLeavesByHospital = patientSickLeavesByHospital.where((elementHospital) => elementHospital.filterName == element.projectName).toList();
if (sickLeavesByHospital.isNotEmpty) {
patientSickLeavesByHospital[patientSickLeavesByHospital.indexOf(sickLeavesByHospital[0])].sickLeavesList!.add(element);
} else {
patientSickLeavesByHospital.add(SickLeaveList(filterName: element.projectName, sickLeaves: element));
}
}
patientSickLeavesViewList = patientSickLeavesByClinic;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);

@ -190,3 +190,16 @@ class PatientMedicalReportResponseModel {
return data;
}
}
class MedicalReportList {
String? filterName;
List<PatientMedicalReportResponseModel>? medicalReportsList;
MedicalReportList({this.filterName, PatientMedicalReportResponseModel? medicalReport}) {
medicalReportsList = [];
if (medicalReport != null) {
medicalReportsList!.add(medicalReport);
}
}
}

@ -174,3 +174,16 @@ class PatientSickLeavesResponseModel {
return data;
}
}
class SickLeaveList {
String? filterName;
List<PatientSickLeavesResponseModel>? sickLeavesList;
SickLeaveList({this.filterName, PatientSickLeavesResponseModel? sickLeaves}) {
sickLeavesList = [];
if (sickLeaves != null) {
sickLeavesList!.add(sickLeaves);
}
}
}

@ -1,7 +1,6 @@
// dart
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/appointment_details_resp_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';

@ -7,7 +7,6 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_
import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_page.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
enum AppointmentViaRegionState {
REGION_SELECTION,

@ -11,7 +11,6 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/rate_appointment_resp_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ask_doctor_request_type_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel;
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_queue_details_response_model.dart';

@ -60,6 +60,12 @@ class MyAppointmentsViewModel extends ChangeNotifier {
List<PatientAppointmentHistoryResponseModel> patientEyeMeasurementsAppointmentsHistoryList = [];
// Grouping by Clinic and Hospital
List<PatientAppointmentList> patientAppointmentsByClinic = [];
List<PatientAppointmentList> patientAppointmentsByHospital = [];
List<PatientAppointmentList> patientAppointmentsViewList = [];
bool isAppointmentsSortByClinic = true;
List<AskDocRequestType> askDoctorRequestTypeList = [];
PatientAppointmentShareResponseModel? patientAppointmentShareResponseModel;
@ -82,6 +88,43 @@ class MyAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
setIsAppointmentsSortByClinic(bool value) {
isAppointmentsSortByClinic = value;
_groupAppointmentsByClinicAndHospital();
notifyListeners();
}
void _groupAppointmentsByClinicAndHospital() {
patientAppointmentsByClinic.clear();
patientAppointmentsByHospital.clear();
for (var element in filteredAppointmentList) {
// Group by clinic
List<PatientAppointmentList> appointmentsByClinic = patientAppointmentsByClinic.where((elementClinic) => elementClinic.filterName == element.clinicName).toList();
if (appointmentsByClinic.isNotEmpty) {
patientAppointmentsByClinic[patientAppointmentsByClinic.indexOf(appointmentsByClinic[0])].patientDoctorAppointmentList!.add(element);
} else {
patientAppointmentsByClinic.add(PatientAppointmentList(filterName: element.clinicName, patientDoctorAppointment: element));
}
// Group by hospital
List<PatientAppointmentList> appointmentsByHospital = patientAppointmentsByHospital.where((elementHospital) => elementHospital.filterName == element.projectName).toList();
if (appointmentsByHospital.isNotEmpty) {
patientAppointmentsByHospital[patientAppointmentsByHospital.indexOf(appointmentsByHospital[0])].patientDoctorAppointmentList!.add(element);
} else {
patientAppointmentsByHospital.add(PatientAppointmentList(filterName: element.projectName, patientDoctorAppointment: element));
}
}
if (isAppointmentsSortByClinic) {
patientAppointmentsViewList = patientAppointmentsByClinic;
} else {
patientAppointmentsViewList = patientAppointmentsByHospital;
}
}
initAppointmentsViewModel() {
if (isAppointmentDataToBeLoaded) {
patientAppointmentsHistoryList.clear();
@ -513,6 +556,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
break;
}
getFiltersForSelectedAppointmentList(filteredAppointmentList);
_groupAppointmentsByClinicAndHospital();
notifyListeners();
}
@ -570,6 +614,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
} catch (e) {}
}
}
_groupAppointmentsByClinicAndHospital();
notifyListeners();
}

@ -110,7 +110,7 @@ class PayfortViewModel extends ChangeNotifier {
onError!(failure.message);
},
(apiResponse) {
log(apiResponse.data);
log(apiResponse.data.toString());
if (onSuccess != null) {
onSuccess(apiResponse);
}
@ -134,7 +134,7 @@ class PayfortViewModel extends ChangeNotifier {
onError!(failure.message);
},
(apiResponse) {
log(apiResponse.data);
log(apiResponse.data.toString());
if (onSuccess != null) {
onSuccess(apiResponse);
}

@ -5,8 +5,6 @@ import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/location/GeocodeResponse.dart';
import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart';
import 'package:hmg_patient_app_new/features/location/PlacePrediction.dart';
import 'package:hmg_patient_app_new/features/location/location_view_model.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/calender_utils_new.dart';

@ -0,0 +1,183 @@
class QrParkingResponseModel {
dynamic totalRecords;
dynamic nRowID;
int? qRParkingID;
String? description;
String? descriptionN;
dynamic qRCompare;
dynamic qRValue;
String? imagePath;
bool? isActive;
int? parkingID;
int? branchID;
int? companyID;
int? buildingID;
int? rowID;
int? gateID;
int? floorID;
dynamic imagePath1;
int? createdBy;
String? createdOn;
dynamic editedBy;
dynamic editedOn;
String? parkingDescription;
String? parkingDescriptionN;
String? gateDescription;
String? gateDescriptionN;
String? branchDescription;
String? branchDescriptionN;
String? companyDescription;
String? companyDescriptionN;
String? rowDescription;
String? rowDescriptionN;
String? floorDescription;
String? floorDescriptionN;
String? buildingDescription;
String? buildingDescriptionN;
String? qRParkingCode;
String? parkingCode;
double? latitude;
double? longitude;
String? qRImageStr;
QrParkingResponseModel({
this.totalRecords,
this.nRowID,
this.qRParkingID,
this.description,
this.descriptionN,
this.qRCompare,
this.qRValue,
this.imagePath,
this.isActive,
this.parkingID,
this.branchID,
this.companyID,
this.buildingID,
this.rowID,
this.gateID,
this.floorID,
this.imagePath1,
this.createdBy,
this.createdOn,
this.editedBy,
this.editedOn,
this.parkingDescription,
this.parkingDescriptionN,
this.gateDescription,
this.gateDescriptionN,
this.branchDescription,
this.branchDescriptionN,
this.companyDescription,
this.companyDescriptionN,
this.rowDescription,
this.rowDescriptionN,
this.floorDescription,
this.floorDescriptionN,
this.buildingDescription,
this.buildingDescriptionN,
this.qRParkingCode,
this.parkingCode,
this.latitude,
this.longitude,
this.qRImageStr,
});
QrParkingResponseModel.fromJson(Map<String, dynamic> json) {
totalRecords = json['TotalRecords'];
nRowID = json['nRowID'];
qRParkingID = json['QRParkingID'];
description = json['Description'];
descriptionN = json['DescriptionN'];
qRCompare = json['QRCompare'];
qRValue = json['QRValue'];
imagePath = json['ImagePath'];
isActive = json['IsActive'];
parkingID = json['ParkingID'];
branchID = json['BranchID'];
companyID = json['CompanyID'];
buildingID = json['BuildingID'];
rowID = json['RowID'];
gateID = json['GateID'];
floorID = json['FloorID'];
imagePath1 = json['ImagePath1'];
createdBy = json['CreatedBy'];
createdOn = json['CreatedOn'];
editedBy = json['EditedBy'];
editedOn = json['EditedOn'];
parkingDescription = json['ParkingDescription'];
parkingDescriptionN = json['ParkingDescriptionN'];
gateDescription = json['GateDescription'];
gateDescriptionN = json['GateDescriptionN'];
branchDescription = json['BranchDescription'];
branchDescriptionN = json['BranchDescriptionN'];
companyDescription = json['CompanyDescription'];
companyDescriptionN = json['CompanyDescriptionN'];
rowDescription = json['RowDescription'];
rowDescriptionN = json['RowDescriptionN'];
floorDescription = json['FloorDescription'];
floorDescriptionN = json['FloorDescriptionN'];
buildingDescription = json['BuildingDescription'];
buildingDescriptionN = json['BuildingDescriptionN'];
qRParkingCode = json['QRParkingCode'];
parkingCode = json['ParkingCode'];
latitude = _toDouble(json['Latitude']);
longitude = _toDouble(json['Longitude']);
qRImageStr = json['QRImageStr'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['TotalRecords'] = totalRecords;
data['nRowID'] = nRowID;
data['QRParkingID'] = qRParkingID;
data['Description'] = description;
data['DescriptionN'] = descriptionN;
data['QRCompare'] = qRCompare;
data['QRValue'] = qRValue;
data['ImagePath'] = imagePath;
data['IsActive'] = isActive;
data['ParkingID'] = parkingID;
data['BranchID'] = branchID;
data['CompanyID'] = companyID;
data['BuildingID'] = buildingID;
data['RowID'] = rowID;
data['GateID'] = gateID;
data['FloorID'] = floorID;
data['ImagePath1'] = imagePath1;
data['CreatedBy'] = createdBy;
data['CreatedOn'] = createdOn;
data['EditedBy'] = editedBy;
data['EditedOn'] = editedOn;
data['ParkingDescription'] = parkingDescription;
data['ParkingDescriptionN'] = parkingDescriptionN;
data['GateDescription'] = gateDescription;
data['GateDescriptionN'] = gateDescriptionN;
data['BranchDescription'] = branchDescription;
data['BranchDescriptionN'] = branchDescriptionN;
data['CompanyDescription'] = companyDescription;
data['CompanyDescriptionN'] = companyDescriptionN;
data['RowDescription'] = rowDescription;
data['RowDescriptionN'] = rowDescriptionN;
data['FloorDescription'] = floorDescription;
data['FloorDescriptionN'] = floorDescriptionN;
data['BuildingDescription'] = buildingDescription;
data['BuildingDescriptionN'] = buildingDescriptionN;
data['QRParkingCode'] = qRParkingCode;
data['ParkingCode'] = parkingCode;
data['Latitude'] = latitude;
data['Longitude'] = longitude;
data['QRImageStr'] = qRImageStr;
return data;
}
static double? _toDouble(dynamic v) {
if (v == null) return null;
if (v is double) return v;
if (v is int) return v.toDouble();
return double.tryParse(v.toString());
}
}

@ -0,0 +1,74 @@
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/features/qr_parking/models/qr_parking_response_model.dart';
import '../../core/api/api_client.dart';
import '../../core/api_consts.dart';
import '../../core/common_models/generic_api_model.dart';
import '../../core/exceptions/api_failure.dart';
import '../../services/logger_service.dart';
abstract class QrParkingRepo {
Future<Either<Failure, GenericApiModel<List<QrParkingResponseModel>>>>
getQrParking({
required int qrParkingId,
});
}
class QrParkingRepoImp implements QrParkingRepo {
final ApiClient apiClient;
final LoggerService loggerService;
QrParkingRepoImp({
required this.loggerService,
required this.apiClient,
});
@override
Future<Either<Failure, GenericApiModel<List<QrParkingResponseModel>>>>
getQrParking({required int qrParkingId}) async {
try {
GenericApiModel<List<QrParkingResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getQrParkingDetails, // GetQRParkingByID
body: {'QRParkingID': qrParkingId},
onFailure: (error, statusCode,
{messageStatus, failureType}) {
failure = failureType ??
StatusCodeFailure("$error ($statusCode)");
},
onSuccess: (response, statusCode,
{messageStatus, errorMessage}) {
final list =
(response['List_SWP_QRParkingModel'] as List?) ?? [];
final res = list
.map((e) => QrParkingResponseModel.fromJson(
Map<String, dynamic>.from(e),
))
.toList();
apiResponse = GenericApiModel<List<QrParkingResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: res,
);
},
);
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,144 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:hmg_patient_app_new/features/qr_parking/qr_parking_repo.dart';
import '../../services/cache_service.dart';
import '../../services/error_handler_service.dart';
import 'models/qr_parking_response_model.dart';
class QrParkingViewModel extends ChangeNotifier {
final QrParkingRepo qrParkingRepo;
final ErrorHandlerService errorHandlerService;
final CacheService cacheService;
String IS_GO_TO_PARKING = 'IS_GO_TO_PARKING';
bool isLoading = false;
String? error;
bool isSavePark = false;
QrParkingResponseModel? qrParkingModel;
List<QrParkingResponseModel> qrParkingList = [];
QrParkingViewModel({
required this.qrParkingRepo,
required this.errorHandlerService,
required this.cacheService,
});
Future<QrParkingResponseModel?> scanAndGetParking() async {
try {
error = null;
isLoading = true;
notifyListeners();
final result = await BarcodeScanner.scan();
if (result.type != ResultType.Barcode) {
isLoading = false;
notifyListeners();
return null;
}
final raw = result.rawContent.trim();
if (raw.isEmpty) {
error = "Invalid QR Code";
isLoading = false;
notifyListeners();
return null;
}
final qrParkingId = _extractQrParkingId(raw);
if (qrParkingId == null) {
error = "Invalid QR Code";
isLoading = false;
notifyListeners();
return null;
}
final apiResult =
await qrParkingRepo.getQrParking(qrParkingId: qrParkingId);
final model = apiResult.fold(
(failure) {
errorHandlerService.handleError(failure: failure);
error = failure.toString();
return null;
},
(apiResponse) {
qrParkingList = apiResponse.data ?? [];
if (qrParkingList.isNotEmpty) {
return qrParkingList.first;
}
error = "Invalid Qr Code";
return null;
},
);
if (model != null) {
qrParkingModel = model;
isSavePark = true;
await cacheService.saveObject(
key: IS_GO_TO_PARKING,
value: model.toJson(),
);
}
isLoading = false;
notifyListeners();
return model;
} catch (e) {
error = "Scan error";
isLoading = false;
notifyListeners();
return null;
}
}
/// Load saved parking
Future<void> getIsSaveParking() async {
isLoading = true;
notifyListeners();
final parking =
await cacheService.getObject(key: IS_GO_TO_PARKING);
if (parking != null) {
isSavePark = true;
qrParkingModel = QrParkingResponseModel.fromJson(
Map<String, dynamic>.from(parking),
);
} else {
isSavePark = false;
qrParkingModel = null;
}
isLoading = false;
notifyListeners();
}
/// Reset parking
Future<void> clearParking() async {
await cacheService.remove(key: IS_GO_TO_PARKING);
isSavePark = false;
qrParkingModel = null;
notifyListeners();
}
int? _extractQrParkingId(String raw) {
try {
if (raw.startsWith("{")) {
final data = jsonDecode(raw);
return int.tryParse(data['QRParkingID'].toString());
}
return int.tryParse(raw);
} catch (_) {
return null;
}
}
}

@ -29,6 +29,18 @@ class RadiologyViewModel extends ChangeNotifier {
late PatientRadiologyResponseModel patientRadiologyOrderByAppointment;
// --- Grouping fields: By Clinic / By Hospital ---
bool isSortByClinic = true;
List<List<PatientRadiologyResponseModel>> patientRadiologyOrdersByClinic = [];
List<List<PatientRadiologyResponseModel>> patientRadiologyOrdersByHospital = [];
List<List<PatientRadiologyResponseModel>> patientRadiologyOrdersViewList = [];
void setIsSortByClinic(bool value) {
isSortByClinic = value;
patientRadiologyOrdersViewList = isSortByClinic ? patientRadiologyOrdersByClinic : patientRadiologyOrdersByHospital;
notifyListeners();
}
RadiologyViewModel({required this.radiologyRepo, required this.errorHandlerService, required this.navigationService});
initRadiologyViewModel() {

@ -1,7 +1,5 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:health/health.dart';
import 'package:permission_handler/permission_handler.dart';

@ -2,7 +2,7 @@
// ignore_for_file: constant_identifier_names
abstract class LocaleKeys {
abstract class LocaleKeys {
static const english = 'english';
static const arabic = 'arabic';
static const login = 'login';
@ -189,7 +189,6 @@ abstract class LocaleKeys {
static const firstName = 'firstName';
static const middleName = 'middleName';
static const lastName = 'lastName';
static const female = 'female';
static const preferredLanguage = 'preferredLanguage';
static const locationsRegister = 'locationsRegister';
static const ksa = 'ksa';
@ -801,7 +800,7 @@ abstract class LocaleKeys {
static const fullName = 'fullName';
static const married = 'married';
static const uae = 'uae';
static const malE = 'male';
static const malE = 'malE';
static const loginBy = 'loginBy';
static const loginByOTP = 'loginByOTP';
static const guest = 'guest';
@ -876,4 +875,6 @@ abstract class LocaleKeys {
static const walkin = 'walkin';
static const laserClinic = 'laserClinic';
static const continueString = 'continueString';
static const covid_info = 'covid_info';
}

@ -1,13 +1,9 @@
import 'dart:async';
import 'dart:collection';
import 'dart:io';
import 'package:device_calendar/device_calendar.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/utils/calender_utils_new.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
@ -41,7 +37,6 @@ import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:maps_launcher/maps_launcher.dart';
import 'package:provider/provider.dart';
import '../../core/dependencies.dart';
import '../medical_file/widgets/medical_file_card.dart';
class AppointmentDetailsPage extends StatefulWidget {

@ -4,6 +4,9 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -35,6 +38,7 @@ class MyAppointmentsPage extends StatefulWidget {
}
class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
int? expandedIndex;
late MyAppointmentsViewModel myAppointmentsViewModel;
late BookAppointmentsViewModel bookAppointmentsViewModel;
@ -68,6 +72,9 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
CustomTabBarModel(null, "Completed".needTranslation),
],
onTabChange: (index) {
setState(() {
expandedIndex = null;
});
myAppointmentsViewModel.onTabChange(index);
myAppointmentsViewModel.updateListWRTTab(index);
context.read<DateRangeSelectorRangeViewModel>().flush();
@ -91,17 +98,55 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
// Clinic & Hospital Sort
Row(
children: [
CustomButton(
text: LocaleKeys.byClinic.tr(context: context),
onPressed: () {
myAppointmentsVM.setIsAppointmentsSortByClinic(true);
},
backgroundColor: myAppointmentsVM.isAppointmentsSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: myAppointmentsVM.isAppointmentsSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
textColor: myAppointmentsVM.isAppointmentsSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
SizedBox(width: 8.h),
CustomButton(
text: LocaleKeys.byHospital.tr(context: context),
onPressed: () {
myAppointmentsVM.setIsAppointmentsSortByClinic(false);
},
backgroundColor: myAppointmentsVM.isAppointmentsSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
borderColor: myAppointmentsVM.isAppointmentsSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor,
textColor: myAppointmentsVM.isAppointmentsSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
).paddingSymmetrical(24.h, 0.h),
Visibility(visible: myAppointmentsVM.availableFilters.isNotEmpty, child: getAppointmentFilters(myAppointmentsVM)),
ListView.separated(
padding: EdgeInsets.only(top: 16.h),
SizedBox(height: 8.h),
// Expandable list
ListView.builder(
padding: EdgeInsets.only(top: 8.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: myAppointmentsVM.isMyAppointmentsLoading
? 5
? 4
: filteredAppointmentList.isNotEmpty
? filteredAppointmentList.length
? myAppointmentsVM.patientAppointmentsViewList.length
: 1,
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return myAppointmentsVM.isMyAppointmentsLoading
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
@ -123,15 +168,149 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: filteredAppointmentList[index],
myAppointmentsViewModel: myAppointmentsViewModel,
bookAppointmentsViewModel: bookAppointmentsViewModel,
isLoading: false,
isFromHomePage: false,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
child: InkWell(
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomButton(
text: "${myAppointmentsVM.patientAppointmentsViewList[index].patientDoctorAppointmentList!.length} Appointments",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
],
),
SizedBox(height: 8.h),
myAppointmentsVM.patientAppointmentsViewList[index].filterName!.toText16(isBold: true)
],
),
),
AnimatedSwitcher(
duration: Duration(milliseconds: 500),
switchInCurve: Curves.easeIn,
switchOutCurve: Curves.easeOut,
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
axisAlignment: 0.0,
child: child,
),
);
},
child: isExpanded
? Container(
key: ValueKey<int>(index),
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...myAppointmentsVM.patientAppointmentsViewList[index].patientDoctorAppointmentList!.map((appointment) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.network(
appointment.doctorImageURL!,
width: 24.h,
height: 24.h,
fit: BoxFit.fill,
).circle(100),
SizedBox(width: 8.h),
Expanded(child: appointment.doctorNameObj!.toText14(weight: FontWeight.w500)),
],
),
SizedBox(height: 8.h),
Row(
children: [
CustomButton(
text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(appointment.appointmentDate), false),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
),
SizedBox(width: 8.h),
CustomButton(
text: myAppointmentsVM.isAppointmentsSortByClinic ? appointment.projectName! : appointment.clinicName!,
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
),
SizedBox(width: 8.h),
CustomButton(
text: appointment.statusDesc ?? "",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
),
],
),
SizedBox(height: 8.h),
AppointmentCard(
patientAppointmentHistoryResponseModel: appointment,
myAppointmentsViewModel: myAppointmentsViewModel,
bookAppointmentsViewModel: bookAppointmentsViewModel,
isLoading: false,
isFromHomePage: false,
),
SizedBox(height: 12.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h),
SizedBox(height: 12.h),
],
);
}),
],
),
)
: SizedBox.shrink(),
),
],
),
),
).paddingSymmetrical(24.h, 0.h),
),
),
),
)
@ -160,8 +339,7 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
).paddingSymmetrical(48.h, 0.h),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 24.h),
],
);

@ -1,6 +1,5 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
@ -21,169 +20,377 @@ import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import '../../widgets/chip/app_custom_chip_widget.dart';
import '../../widgets/buttons/custom_button.dart';
class MyDoctorsPage extends StatelessWidget {
class MyDoctorsPage extends StatefulWidget {
MyDoctorsPage({super.key});
@override
State<MyDoctorsPage> createState() => _MyDoctorsPageState();
}
class _MyDoctorsPageState extends State<MyDoctorsPage> {
late MyAppointmentsViewModel myAppointmentsViewModel;
late BookAppointmentsViewModel bookAppointmentsViewModel;
bool isSortByClinic = true; // default like lab page
int? expandedIndex;
final Map<int, GlobalKey> _groupKeys = {};
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
return CollapsingListView(
title: LocaleKeys.myDoctor.tr(context: context),
child: SingleChildScrollView(
child: Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
child: Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
// build grouped lists from the flat list
final clinicMap = <String, List<dynamic>>{};
final hospitalMap = <String, List<dynamic>>{};
for (var doc in myAppointmentsVM.patientMyDoctorsList) {
final clinicKey = (doc.clinicName ?? 'Unknown').trim();
clinicMap.putIfAbsent(clinicKey, () => []).add(doc);
final hospitalKey = (doc.projectName ?? doc.projectID?.toString() ?? 'Unknown').toString().trim();
hospitalMap.putIfAbsent(hospitalKey, () => []).add(doc);
}
final patientMyDoctorsByClinic = clinicMap.values.toList();
final patientMyDoctorsByHospital = hospitalMap.values.toList();
final patientMyDoctorsViewList = isSortByClinic ? patientMyDoctorsByClinic : patientMyDoctorsByHospital;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
// Add Clinic / Hospital toggle buttons styled like LabOrdersPage
Padding(
padding: EdgeInsets.symmetric(horizontal: 24.h),
child: Row(
children: [
CustomButton(
text: LocaleKeys.byClinic.tr(context: context),
onPressed: () {
setState(() {
isSortByClinic = true;
expandedIndex = null;
});
},
backgroundColor: isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
textColor: isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
SizedBox(width: 8.h),
CustomButton(
text: LocaleKeys.byHospital.tr(context: context),
onPressed: () {
setState(() {
isSortByClinic = false;
expandedIndex = null;
});
},
backgroundColor: isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
borderColor: isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor,
textColor: isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
),
),
SizedBox(height: 16.h),
ListView.separated(
scrollDirection: Axis.vertical,
itemCount: myAppointmentsVM.isPatientMyDoctorsLoading ? 5 : myAppointmentsVM.patientMyDoctorsList.length,
itemCount: myAppointmentsVM.isPatientMyDoctorsLoading ? 5 : (patientMyDoctorsViewList.isNotEmpty ? patientMyDoctorsViewList.length : 0),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.only(left: 24.h, right: 24.h),
itemBuilder: (context, index) {
return myAppointmentsVM.isPatientMyDoctorsLoading
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(14.h),
child: Column(
if (myAppointmentsVM.isPatientMyDoctorsLoading) {
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: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 63.h,
height: 63.h,
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: true),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Dr John Smith".toText16(isBold: true).toShimmer2(isShow: true),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
],
),
],
),
),
],
),
],
),
),
)
: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 1000),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(14.h),
Image.network(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 63.h,
height: 63.h,
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: true),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!,
width: 63.h,
height: 63.h,
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: false),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(myAppointmentsVM.patientMyDoctorsList[index].doctorName).toString().toText16(isBold: true).toShimmer2(isShow: false),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: myAppointmentsVM.patientMyDoctorsList[index].clinicName).toShimmer2(isShow: false, width: 16.h),
AppCustomChipWidget(labelText: myAppointmentsVM.patientMyDoctorsList[index].projectName).toShimmer2(isShow: false, width: 16.h),
],
),
],
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
"Dr John Smith".toText16(isBold: true).toShimmer2(isShow: true),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
"".toText16(),
Transform.flip(
flipX: appState.isArabic(),
child:
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon_small, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)),
AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
],
),
],
),
),
).onPress(() async {
bookAppointmentsViewModel.setSelectedDoctor(DoctorsListResponseModel(
clinicID: myAppointmentsVM.patientMyDoctorsList[index].clinicID,
projectID: myAppointmentsVM.patientMyDoctorsList[index].projectID,
doctorID: myAppointmentsVM.patientMyDoctorsList[index].doctorID,
));
LoaderBottomSheet.showLoader();
await bookAppointmentsViewModel.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorProfilePage(),
),
);
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
}),
],
),
],
),
),
);
}
final group = patientMyDoctorsViewList[index];
final displayName = isSortByClinic ? (group.first.clinicName ?? 'Unknown') : (group.first.projectName ?? 'Unknown');
final isExpanded = expandedIndex == index;
return Container(
key: _groupKeys.putIfAbsent(index, () => GlobalKey()),
margin: EdgeInsets.only(bottom: 12.h),
padding: EdgeInsets.all(16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
WidgetsBinding.instance.addPostFrameCallback((_) {
final key = _groupKeys[index];
if (key != null && key.currentContext != null && expandedIndex == index) {
Scrollable.ensureVisible(
key.currentContext!,
duration: Duration(milliseconds: 350),
curve: Curves.easeInOut,
alignment: 0.1,
);
}
});
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomButton(
text: "${group.length} ${'doctors'.needTranslation}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
Icon(isExpanded ? Icons.expand_less : Icons.chevron_right, color: AppColors.greyColor),
],
),
SizedBox(height: 8.h),
Text(
displayName,
style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600),
overflow: TextOverflow.ellipsis,
),
],
),
);
),
AnimatedSwitcher(
duration: Duration(milliseconds: 400),
child: isExpanded
? Container(
key: ValueKey<int>(index),
padding: EdgeInsets.only(top: 12.h),
child: Column(
children: group.map<Widget>((doctor) {
return Container(
margin: EdgeInsets.only(bottom: 12.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 12.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
(doctor?.doctorImageURL ?? doctor?.doctorImage ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png"),
width: 24.h,
height: 24.h,
fit: BoxFit.cover,
).circle(100),
SizedBox(width: 8.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(doctor?.doctorName ?? "").toString().toText14(weight: FontWeight.w500),
SizedBox(height: 6.h),
],
),
),
],
),
SizedBox(height: 8.h),
Row(
children: [
CustomButton(
text: isSortByClinic ? (doctor?.clinicName ?? "") : (doctor?.projectName ?? ""),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
),
],
),
SizedBox(height: 8.h),
Row(
children: [
Expanded(
flex: 6,
child: CustomButton(
text: "View Profile".needTranslation.tr(context: context),
onPressed: () async {
bookAppointmentsViewModel.setSelectedDoctor(DoctorsListResponseModel(
clinicID: doctor?.clinicID ?? 0,
projectID: doctor?.projectID ?? 0,
doctorID: doctor?.doctorID ?? 0,
));
LoaderBottomSheet.showLoader();
await bookAppointmentsViewModel.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorProfilePage(),
),
);
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
},
backgroundColor: AppColors.bgRedLightColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
),
SizedBox(width: 8.h),
Expanded(
flex: 1,
child: Container(
height: 40.h,
width: 40.w,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.textColor,
borderRadius: 12,
),
child: Padding(
padding: EdgeInsets.all(12.h),
child: Transform.flip(
flipX: getIt<AppState>().isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.whiteColor,
fit: BoxFit.contain,
),
),
),
).onPress(() async {
bookAppointmentsViewModel.setSelectedDoctor(DoctorsListResponseModel(
clinicID: doctor?.clinicID ?? 0,
projectID: doctor?.projectID ?? 0,
doctorID: doctor?.doctorID ?? 0,
));
LoaderBottomSheet.showLoader();
await bookAppointmentsViewModel.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorProfilePage(),
),
);
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
}),
),
],
),
],
),
),
);
}).toList(),
),
)
: SizedBox.shrink(),
),
],
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
@ -191,8 +398,6 @@ class MyDoctorsPage extends StatelessWidget {
],
);
}),
),
),
);
}
}

@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/appointemnet_filters.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:smooth_corner/smooth_corner.dart';

@ -17,7 +17,6 @@ import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_vie
import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/appointment_details_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/appointment_queue_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/ask_doctor_request_type_select.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/eye_measurement_details_page.dart';
@ -353,14 +352,14 @@ class AppointmentCard extends StatelessWidget {
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 14.f,
fontSize: 12.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 40.h,
icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor,
iconSize: 16.h,
iconSize: 14.h,
);
}

@ -14,7 +14,6 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/appointment_queue_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:barcode_scan2/barcode_scan2.dart';
@ -22,7 +21,6 @@ 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/nfc/nfc_reader_sheet.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
class AppointmentCheckinBottomSheet extends StatelessWidget {
AppointmentCheckinBottomSheet({super.key, required this.patientAppointmentHistoryResponseModel, required this.myAppointmentsViewModel});
@ -60,7 +58,7 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
double dist = Utils.distance(value.latitude, value.longitude, double.parse(projectDetailListModel.latitude!), double.parse(projectDetailListModel.longitude!)).ceilToDouble() * 1000;
print(dist);
if (dist <= projectDetailListModel.geofenceRadius!) {
sendCheckInRequest(projectDetailListModel.checkInQrCode!, context);
sendCheckInRequest(projectDetailListModel.checkInQrCode!, 3, context);
} else {
showCommonBottomSheetWithoutHeight(context,
title: "Error".needTranslation,
@ -79,7 +77,7 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
Future.delayed(const Duration(milliseconds: 500), () {
showNfcReader(context, onNcfScan: (String nfcId) {
Future.delayed(const Duration(milliseconds: 100), () {
sendCheckInRequest(nfcId, context);
sendCheckInRequest(nfcId, 1, context);
});
}, onCancel: () {});
});
@ -92,7 +90,7 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
).onPress(() async {
String onlineCheckInQRCode = (await BarcodeScanner.scan().then((value) => value.rawContent));
if (onlineCheckInQRCode != "") {
sendCheckInRequest(onlineCheckInQRCode, context);
sendCheckInRequest(onlineCheckInQRCode, 2, context);
} else {}
}),
],
@ -140,12 +138,12 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
);
}
void sendCheckInRequest(String scannedCode, BuildContext context) async {
void sendCheckInRequest(String scannedCode, int checkInType, BuildContext context) async {
LoaderBottomSheet.showLoader(loadingText: "Processing Check-In...".needTranslation);
await myAppointmentsViewModel.sendCheckInNfcRequest(
patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
scannedCode: scannedCode,
checkInType: 2,
checkInType: checkInType,
onSuccess: (apiResponse) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(context, title: "Success".needTranslation, child: Utils.getSuccessWidget(loadingText: LocaleKeys.success.tr()), callBackFunc: () async {

@ -167,14 +167,14 @@ class AppointmentDoctorCard extends StatelessWidget {
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 14.f,
fontSize: 12.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0),
height: 40.h,
icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor,
iconSize: 16.h,
iconSize: 14.h,
);
} else {
return patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false

@ -1,7 +1,6 @@
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/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';

@ -4,10 +4,8 @@ import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
class FacilitySelectionItem extends StatelessWidget {

@ -8,8 +8,6 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'
show MyAppointmentsViewModel;
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_selection_item.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';

@ -1,21 +1,14 @@
import 'package:easy_localization/easy_localization.dart'
show tr, StringTranslateExtension;
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:provider/provider.dart';
class HospitalBottomSheetBodyForDoctorFilter extends StatelessWidget {

@ -7,7 +7,6 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';

@ -4,7 +4,6 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:provider/provider.dart' show Consumer;

@ -5,7 +5,6 @@ import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;

@ -6,7 +6,6 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils;
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart' show MyAppointmentsViewModel;
import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart' show RegionListItem;
import 'package:provider/provider.dart';

@ -5,7 +5,6 @@ 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/blood_donation/blood_donation_view_model.dart';
import 'package:hmg_patient_app_new/presentation/blood_donation/widgets/city_list_item.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class SelectBloodGroupWidget extends StatelessWidget {

@ -1,10 +1,8 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart';
import 'package:hmg_patient_app_new/presentation/blood_donation/widgets/city_list_item.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class SelectCityWidget extends StatelessWidget {
SelectCityWidget({super.key, required this.bloodDonationViewModel});

@ -5,7 +5,6 @@ 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/blood_donation/blood_donation_view_model.dart';
import 'package:hmg_patient_app_new/presentation/blood_donation/widgets/city_list_item.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class SelectGenderWidget extends StatelessWidget {

@ -1,8 +1,6 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'
show BookAppointmentsViewModel;
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';

@ -2,26 +2,16 @@ import 'package:easy_localization/easy_localization.dart'
show tr, StringTranslateExtension;
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_filter/clinic_item.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/clinic_card.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:provider/provider.dart';
import '../../../features/book_appointments/models/resp_models/get_clinic_list_response_model.dart' show GetClinicsListResponseModel;
class ClinicBottomSheet extends StatelessWidget {
late BookAppointmentsViewModel appointmentsViewModel;

@ -9,7 +9,6 @@ import 'package:hmg_patient_app_new/features/book_appointments/book_appointments
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body_for_doctor_filter.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_filter/RegionChips.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_filter/clinic_bottomsheet.dart';

@ -4,8 +4,6 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'
show BookAppointmentsViewModel;
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';

@ -1,18 +1,13 @@
import 'dart:math';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.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/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';

@ -1,7 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/laser_body_parts.dart';
@ -82,7 +81,7 @@ class LaserAppointment extends StatelessWidget {
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null,LocaleKeys.malE.tr()),
CustomTabBarModel(null,LocaleKeys.female.tr()),
CustomTabBarModel(null,"Female".needTranslation.tr()),
],
onTabChange: (index) {
var viewmodel = context.read<BookAppointmentsViewModel>();

@ -1,7 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/LaserCategoryType.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';

@ -1,3 +1,4 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
@ -322,15 +323,29 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
}
Future<bool> askVideoCallPermission() async {
Map<Permission, PermissionStatus> statuses = await [Permission.camera, Permission.microphone, Permission.notification].request();
bool hasPermission = false;
if (Platform.isIOS) {
Map<Permission, PermissionStatus> statuses = await [Permission.camera, Permission.microphone].request();
if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted && statuses[Permission.notification] == PermissionStatus.granted) {
// Camera permission granted
return true;
if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted) {
// Camera permission granted
hasPermission = true;
} else {
hasPermission = false;
}
} else {
return false;
Map<Permission, PermissionStatus> statuses = await [Permission.camera, Permission.microphone, Permission.notification].request();
if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted && statuses[Permission.notification] == PermissionStatus.granted) {
// Camera permission granted
hasPermission = true;
} else {
hasPermission = false;
}
}
return hasPermission;
// if (!(await Permission.camera.request().isGranted) || !(await Permission.microphone.request().isGranted)) {
// return false;
// }

@ -11,30 +11,25 @@ import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import 'package:smooth_corner/smooth_corner.dart';

@ -1,6 +1,5 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
@ -11,13 +10,11 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/countdown_timer.dart';
import 'package:lottie/lottie.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';

@ -7,7 +7,6 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';

@ -6,10 +6,7 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class SelectLiveCareCallType extends StatelessWidget {

@ -21,7 +21,6 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/loading_dialog.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';

@ -2,8 +2,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -21,7 +19,6 @@ 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:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
import '../../features/book_appointments/models/resp_models/doctors_list_response_model.dart';

@ -33,7 +33,6 @@ 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:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
class SelectClinicPage extends StatefulWidget {
@ -82,7 +81,14 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
child: CustomButton(
text: LocaleKeys.bookAppo.tr(),
onPressed: () {
onClinicSelected(GetClinicsListResponseModel(clinicID: 108, clinicDescription: "asdfds", clinicDescriptionN: "asdfds"));
if (widget.calculatedResult["clinicId"] == null) {
bookAppointmentsViewModel.setIsDoctorsListLoading(true);
bookAppointmentsViewModel.isGetDocForHealthCal = true;
bookAppointmentsViewModel.calculationID = widget.calculatedResult["calculationID"];
Navigator.push(context, CustomPageRoute(page: SelectDoctorPage()));
} else {
onClinicSelected(GetClinicsListResponseModel(clinicID: widget.calculatedResult["clinicId"], clinicDescription: "asdfds", clinicDescriptionN: "asdfds"));
}
},
icon: null,
fontSize: 16.f,
@ -248,7 +254,6 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
}
Widget resultWidget({required HealthCalculatorsTypeEnum type, dynamic calculatedResult}) {
print(calculatedResult);
return Column(
children: [
if (type == HealthCalculatorsTypeEnum.bmi) ...[
@ -747,8 +752,6 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
],
),
),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
@ -793,7 +796,6 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
],
),
),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
@ -838,8 +840,6 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
],
),
),
],
)
],

@ -17,6 +17,7 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/doctor_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/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';
@ -32,20 +33,30 @@ class SelectDoctorPage extends StatefulWidget {
class _SelectDoctorPageState extends State<SelectDoctorPage> {
TextEditingController searchEditingController = TextEditingController();
int? expandedIndex;
FocusNode textFocusNode = FocusNode();
late AppState appState;
late BookAppointmentsViewModel bookAppointmentsViewModel;
// Scroll controller to control page scrolling when a group expands
late ScrollController _scrollController;
// Map of keys for each item to allow scrolling to them
final Map<int, GlobalKey> _itemKeys = {};
@override
void initState() {
_scrollController = ScrollController();
scheduleMicrotask(() {
bookAppointmentsViewModel.setIsNearestAppointmentSelected(false);
if (bookAppointmentsViewModel.isLiveCareSchedule) {
bookAppointmentsViewModel.getLiveCareDoctorsList();
} else {
if (bookAppointmentsViewModel.selectedClinic.clinicID == 17) {
bookAppointmentsViewModel.getDentalChiefComplaintDoctorsList();
} else if (bookAppointmentsViewModel.isGetDocForHealthCal) {
bookAppointmentsViewModel.getDoctorsListByHealthCal();
} else {
bookAppointmentsViewModel.getDoctorsList();
}
@ -54,6 +65,12 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
super.initState();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
@ -63,6 +80,7 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
body: CollapsingListView(
title: "Choose Doctor".needTranslation,
child: SingleChildScrollView(
controller: _scrollController,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.h),
child: Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
@ -105,23 +123,80 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
),
],
),
SizedBox(height: 16.h),
Row(
children: [
CustomButton(
text: LocaleKeys.byClinic.tr(context: context),
onPressed: () {
bookAppointmentsVM.setIsSortByClinic(true);
},
backgroundColor: bookAppointmentsVM.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: bookAppointmentsVM.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2),
textColor: bookAppointmentsVM.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
SizedBox(width: 8.h),
CustomButton(
text: LocaleKeys.byHospital.tr(context: context),
onPressed: () {
bookAppointmentsVM.setIsSortByClinic(false);
},
backgroundColor: bookAppointmentsVM.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
borderColor: bookAppointmentsVM.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor,
textColor: bookAppointmentsVM.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
).paddingSymmetrical(0.h, 0.h),
SizedBox(height: 16.h),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.nearestAppo.tr(context: context).toText13(isBold: true),
SizedBox(height: 4.h),
"View nearest available appointments".needTranslation.toText11(color: AppColors.textColorLight, weight: FontWeight.w500),
],
),
const Spacer(),
Switch(
activeThumbColor: AppColors.successColor,
activeTrackColor: AppColors.successColor.withValues(alpha: .15),
value: bookAppointmentsVM.isNearestAppointmentSelected,
onChanged: (newValue) async {
bookAppointmentsVM.setIsNearestAppointmentSelected(newValue);
},
),
],
),
ListView.separated(
padding: EdgeInsets.only(top: 24.h),
padding: EdgeInsets.only(top: 16.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: bookAppointmentsVM.isDoctorsListLoading
? 5
: (bookAppointmentsVM.isLiveCareSchedule
? (bookAppointmentsVM.liveCareDoctorsList.isNotEmpty ? bookAppointmentsVM.liveCareDoctorsList.length : 1)
: (bookAppointmentsVM.doctorsList.isNotEmpty ? bookAppointmentsVM.doctorsList.length : 1)),
: (bookAppointmentsVM.doctorsListGrouped.isNotEmpty ? bookAppointmentsVM.doctorsListGrouped.length : 1),
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return bookAppointmentsVM.isDoctorsListLoading
? DoctorCard(
doctorsListResponseModel: DoctorsListResponseModel(),
isLoading: true,
bookAppointmentsViewModel: bookAppointmentsViewModel,
)
: checkIsDoctorsListEmpty()
: bookAppointmentsVM.doctorsListGrouped.isEmpty
? Utils.getNoDataWidget(context, noDataText: "No Doctor found for selected criteria...".needTranslation)
: AnimationConfiguration.staggeredList(
position: index,
@ -130,40 +205,107 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
key: _itemKeys.putIfAbsent(index, () => GlobalKey()),
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: DoctorCard(
doctorsListResponseModel: bookAppointmentsVM.isLiveCareSchedule
? bookAppointmentsVM.liveCareDoctorsList[index]
: bookAppointmentsVM.doctorsList[index],
isLoading: false,
bookAppointmentsViewModel: bookAppointmentsViewModel,
).onPress(() async {
bookAppointmentsVM.setSelectedDoctor(bookAppointmentsVM.isLiveCareSchedule
? bookAppointmentsVM.liveCareDoctorsList[index]
: bookAppointmentsVM.doctorsList[index]);
// bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel());
LoaderBottomSheet.showLoader();
await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorProfilePage(),
),
);
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
}),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: InkWell(
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
// After rebuild, ensure the expanded item is visible
WidgetsBinding.instance.addPostFrameCallback((_) {
final key = _itemKeys[index];
if (key != null && key.currentContext != null && expandedIndex == index) {
Scrollable.ensureVisible(
key.currentContext!,
duration: Duration(milliseconds: 350),
curve: Curves.easeInOut,
alignment: 0.1,
);
}
});
},
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header row with count badge and expand/collapse icon
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomButton(
text: "${bookAppointmentsVM.doctorsListGrouped[index].length} ${'doctors'.needTranslation}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
],
),
SizedBox(height: 8.h),
// Clinic/Hospital name as group title
Text(
bookAppointmentsVM.isSortByClinic
? (bookAppointmentsVM.doctorsListGrouped[index].first.clinicName ?? 'Unknown')
: (bookAppointmentsVM.doctorsListGrouped[index].first.projectName ?? 'Unknown'),
style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600),
overflow: TextOverflow.ellipsis,
),
// Expanded content - list of doctors in this group
AnimatedSwitcher(
duration: Duration(milliseconds: 400),
child: isExpanded
? Container(
key: ValueKey<int>(index),
padding: EdgeInsets.only(top: 12.h),
child: Column(
children: bookAppointmentsVM.doctorsListGrouped[index].map<Widget>((doctor) {
return Container(
margin: EdgeInsets.only(bottom: 12.h),
child: DoctorCard(
doctorsListResponseModel: doctor,
isLoading: false,
bookAppointmentsViewModel: bookAppointmentsViewModel,
).onPress(() async {
bookAppointmentsVM.setSelectedDoctor(doctor);
LoaderBottomSheet.showLoader();
await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorProfilePage(),
),
);
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
}),
);
}).toList(),
),
)
: SizedBox.shrink(),
),
],
),
),
),
),
),
),

@ -1,4 +1,3 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_nfc_kit/flutter_nfc_kit.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';

@ -1,7 +1,6 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
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';
@ -21,7 +20,6 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:lottie/lottie.dart';
import 'package:provider/provider.dart';
import 'package:smooth_corner/smooth_corner.dart';

@ -110,17 +110,19 @@ class DoctorCard extends StatelessWidget {
AppCustomChipWidget(
labelText: "Branch: ${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}".needTranslation,
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
doctorsListResponseModel.decimalDoctorRate != null ? AppCustomChipWidget(
icon: AppAssets.rating_icon,
iconColor: AppColors.ratingColorYellow,
labelText: "Rating: ${isLoading ? 4.78 : doctorsListResponseModel.decimalDoctorRate}".needTranslation,
).toShimmer2(isShow: isLoading),
doctorsListResponseModel.nearestFreeSlot != null
).toShimmer2(isShow: isLoading) : SizedBox(),
bookAppointmentsViewModel.isNearestAppointmentSelected
? doctorsListResponseModel.nearestFreeSlot != null
? AppCustomChipWidget(
labelText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot)).needTranslation,
backgroundColor: AppColors.successColor,
textColor: AppColors.whiteColor,
).toShimmer2(isShow: isLoading)
: SizedBox.shrink()
: SizedBox.shrink(),
],
),

@ -8,13 +8,10 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/feedback_type.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_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/contact_us/widgets/feedback_appointment_selection.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';

@ -4,14 +4,11 @@ import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/widgets/find_us_item_card.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_sick_leave_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/chip/app_custom_chip_widget.dart';

@ -4,13 +4,11 @@ import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_hmg_locations.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:maps_launcher/maps_launcher.dart';
import 'package:url_launcher/url_launcher.dart';

@ -0,0 +1,283 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/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/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import 'covid_19_questionnaire.dart';
class Covid19LandingPage extends StatefulWidget {
const Covid19LandingPage({super.key});
@override
State<Covid19LandingPage> createState() => _Covid19LandingPageState();
}
class _Covid19LandingPageState extends State<Covid19LandingPage> {
late HabibWalletViewModel habibWalletVM;
int? _selectedBranchIndex;
@override
void initState() {
habibWalletVM = Provider.of<HabibWalletViewModel>(context, listen: false);
scheduleMicrotask(() {
getProjectList();
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(
children: [
Expanded(
child: CollapsingListView(
title: "COVID-19",
child: Padding(
padding: EdgeInsets.all(24.w),
child: SingleChildScrollView(
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(20.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Get the results in Few Hours"
.toText18(
color: AppColors.textColor,
weight: FontWeight.w600,
),
SizedBox(height: 16.h),
LocaleKeys.covid_info
.tr()
.toText14(
color: AppColors.greyTextColor,
weight: FontWeight.w400,
height: 1.6,
),
],
),
),
),
),
),
),
),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
// borderRadius: 24.r,
hasShadow: true,
customBorder: BorderRadius.only(topLeft:Radius.circular(24.r) , topRight:Radius.circular(24.r))
),
child: SizedBox(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomButton(
text: "SelectLocation".needTranslation,
onPressed: () {
_showBranchBottomSheet(context);
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 50.h,
iconSize: 18.h,
).paddingSymmetrical(16.h, 24.w),
],
),
),
),
],
));
}
void _showBranchBottomSheet(BuildContext context, ) {
// Set first branch as selected by default
setState(() {
_selectedBranchIndex = 0;
});
showCommonBottomSheet(
context,
title: "Select Branch".needTranslation,
height: ResponsiveExtension.screenHeight * 0.651,
child: StatefulBuilder(
builder: (context, setBottomSheetState) {
return Consumer<HabibWalletViewModel>(
builder: (context, habibWalletVM, child) {
final hospitals = habibWalletVM.advancePaymentHospitals;
if (hospitals.isEmpty) {
return const Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Text('No branches available'),
),
);
}
return Column(
children: [
Expanded(
child:Container(
margin: EdgeInsets.only(left: 16.w, right: 16.w, top: 12.h, bottom: 24.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
hasShadow: true,
customBorder: BorderRadius.only(
topLeft: Radius.circular(24.r),
topRight: Radius.circular(24.r),
),
), child: ListView.separated(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
itemBuilder: (context, index) {
final branch = hospitals[index];
final isSelected = _selectedBranchIndex == index;
return GestureDetector(
onTap: () {
setBottomSheetState(() {
_selectedBranchIndex = index;
});
setState(() {
_selectedBranchIndex = index;
});
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
child: Row(
children: [
// Radio button
Container(
width: 20.w,
height: 20.h,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: isSelected
? AppColors.primaryRedColor
: AppColors.greyTextColor.withValues(alpha: 0.3),
width: 2,
),
),
child: isSelected
? Center(
child: Container(
width: 10.w,
height: 10.h,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColors.primaryRedColor,
),
),
)
: null,
),
SizedBox(width: 12.w),
// Branch details
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 4.h),
(branch.desciption ?? 'Unknown').toText14(
color: AppColors.textColor,
weight: FontWeight.w600,
),
],
),
),
// Location icon
],
),
),
);
},
separatorBuilder: (context, index) => SizedBox(height: 12.h),
itemCount: hospitals.length,
)),
),
// Next button
Container(
padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
hasShadow: true,
customBorder: BorderRadius.only(
topLeft: Radius.circular(24.r),
topRight: Radius.circular(24.r),
),
),
child: SafeArea(
top: false,
child: CustomButton(
text: "Next".needTranslation,
onPressed: (){
Navigator.of(context)
.push(
CustomPageRoute(
page: Covid19Questionnaire(selectedHospital: hospitals[_selectedBranchIndex!],),
),
);
},
backgroundColor: _selectedBranchIndex != null
? AppColors.primaryRedColor
: AppColors.greyTextColor.withValues(alpha: 0.3),
borderColor: _selectedBranchIndex != null
? AppColors.primaryRedColor
: AppColors.greyTextColor.withValues(alpha: 0.3),
textColor: AppColors.whiteColor,
fontSize: 16.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
height: 56.h,
),
),
),
],
);
},
);
},
),
);
}
getProjectList() async{
await habibWalletVM.getProjectsList();
}
}

@ -0,0 +1,132 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/covid_questionnare_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/CustomSwitch.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:provider/provider.dart';
class Covid19Questionnaire extends StatefulWidget {
final HospitalsModel selectedHospital;
const Covid19Questionnaire({super.key, required this.selectedHospital});
@override
State<Covid19Questionnaire> createState() => _Covid19QuestionnaireState();
}
class _Covid19QuestionnaireState extends State<Covid19Questionnaire> {
late HmgServicesViewModel hmgServicesViewModel;
List<CovidQuestionnaireModel> qaList = [];
@override
void initState() {
hmgServicesViewModel = Provider.of<HmgServicesViewModel>(context, listen: false);
scheduleMicrotask(() {
setState(() {
qaList = hmgServicesViewModel.getQuestionsFromJson();
});
});
super.initState();
}
void _toggleAnswer(int index, bool value) {
setState(() {
qaList[index].ans = value ? 1 : 0;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(children: [
Expanded(
child: CollapsingListView(
title: "COVID-19",
child: Padding(
padding: EdgeInsets.all(24.w),
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(20.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Please answer below questionnaire:".toText14(
color: AppColors.textColor,
weight: FontWeight.w500,
),
SizedBox(height: 20.h),
// Question list
ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: qaList.length,
separatorBuilder: (context, index) => SizedBox(height: 16.h),
itemBuilder: (context, index) {
final question = qaList[index];
final isAnswerYes = question.ans == 1;
return Row(
children: [
Expanded(
child: (question.questionEn ?? '').toText14(
color: AppColors.textColor,
weight: FontWeight.w400,
),
),
SizedBox(width: 12.w),
CustomSwitch(
value: isAnswerYes,
onChanged: (value) => _toggleAnswer(index, value),
),
],
);
},
),
],
),
),
),
),
),
SizedBox(height: 16.h),
// Next button
CustomButton(
text: "Next".needTranslation,
onPressed: () {
// Handle next action
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
height: 56.h,
),
],
),
),
),
),
]));
}
}

@ -1,6 +1,5 @@
// managers/referral_form_manager.dart
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart';

@ -1,4 +1,3 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';

@ -1,5 +1,4 @@
import 'dart:io';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/utils/validation_utils.dart';
@ -233,7 +232,6 @@ class _OtherDetailsStepState extends State<OtherDetailsStep> {
}
void _showBranchBottomSheet(BuildContext context, ReferralFormManager formManager) {
final habibWalletVM = context.read<HabibWalletViewModel>();
showCommonBottomSheetWithoutHeight(
context,

@ -1,4 +1,3 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@ -8,13 +7,11 @@ 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/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/AmbulanceCallingPlace.dart';
import 'package:hmg_patient_app_new/features/location/GeocodeResponse.dart';
import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart';
import 'package:hmg_patient_app_new/features/location/PlacePrediction.dart';
import 'package:hmg_patient_app_new/features/location/location_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_doctor_card.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/AddressItem.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/appointment_bottom_sheet.dart' show AppointmentBottomSheet;

@ -1,5 +1,4 @@
import 'package:easy_localization/easy_localization.dart' show tr, StringTranslateExtension;
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';
@ -11,8 +10,6 @@ import 'package:hmg_patient_app_new/features/emergency_services/models/resp_mode
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/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/radio/custom_radio_button.dart';
import 'package:provider/provider.dart';
class RrtRequestTypeSelect extends StatelessWidget {

@ -1,14 +1,10 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/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/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:provider/provider.dart';
class TermsAndCondition extends StatelessWidget {
final String termsAndCondition;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save