Compare commits
	
		
			8 Commits 
		
	
	
		
			4e17ff9c82
			...
			fbd6ef0cf8
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | fbd6ef0cf8 | 1 month ago | 
|  | 62b103e233 | 1 month ago | 
|  | 9f95150a94 | 1 month ago | 
|  Haroon Amjad | 2aa84e0334 | 2 months ago | 
|  | 6f745f3666 | 2 months ago | 
|  | 73f8baa77e | 2 months ago | 
|  | 7476c8e55a | 2 months ago | 
|  | 3066bc8ea3 | 2 months ago | 
| @ -0,0 +1,4 @@ | |||||||
|  | <svg width="58" height="58" viewBox="0 0 58 58" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <rect width="58" height="58" rx="12" fill="#FBCB6E" fill-opacity="0.15"/> | ||||||
|  |     <path d="M25.0916 14.667C27.2323 14.667 28.9404 14.6669 30.28 14.8467C31.6637 15.0324 32.8033 15.4263 33.7048 16.3262C34.9739 17.5929 35.2497 18.0132 35.3328 20.2969C35.3528 20.8488 34.9217 21.3129 34.3699 21.333C33.818 21.3531 33.3539 20.922 33.3337 20.3701C33.2507 18.0876 32.9594 18.4076 32.2917 17.7412C31.824 17.2744 31.1767 16.9851 30.0144 16.8291C29.9521 16.8207 29.8884 16.8122 29.824 16.8047C29.7172 17.4421 29.6185 18.0004 29.4851 18.4551C29.3263 18.9961 29.0889 19.5045 28.6306 19.9111C28.1565 20.3318 27.61 20.508 27.0251 20.5889C26.4737 20.6651 25.7928 20.667 24.9998 20.667C24.2069 20.667 23.5267 20.665 22.9753 20.5889C22.3904 20.508 21.844 20.3318 21.3699 19.9111C20.9116 19.5045 20.6732 18.9961 20.5144 18.4551C20.3813 18.0015 20.2829 17.4451 20.1765 16.8096C20.1237 16.8159 20.0716 16.8222 20.0203 16.8291C18.858 16.9851 18.2107 17.2745 17.7429 17.7412C17.2753 18.2079 16.985 18.8541 16.8289 20.0137C16.6688 21.2021 16.6667 22.7723 16.6667 25V33C16.6667 35.2281 16.6688 36.7988 16.8289 37.9873C16.985 39.1469 17.2753 39.7921 17.7429 40.2588C18.2108 40.7257 18.8577 41.0159 20.0203 41.1719C21.2114 41.3317 22.7852 41.334 25.0173 41.334C27.2495 41.334 28.8233 41.3317 30.0144 41.1719C31.1769 41.0159 31.8239 40.7258 32.2917 40.2588C32.9594 39.5924 33.2507 38.5793 33.3337 36.2969C33.354 35.7452 33.8181 35.3139 34.3699 35.334C34.9217 35.3541 35.3528 35.8182 35.3328 36.3701C35.2497 38.6542 34.974 40.408 33.7048 41.6748C32.8033 42.5746 31.6636 42.9686 30.28 43.1543C28.9404 43.334 27.2323 43.334 25.0916 43.334H24.9431C22.8025 43.334 21.0942 43.334 19.7546 43.1543C18.3711 42.9686 17.2313 42.5745 16.3298 41.6748C15.4281 40.7748 15.0326 39.6367 14.8464 38.2549C14.6663 36.9174 14.6667 35.2117 14.6667 33.0752V24.9258C14.6667 22.7891 14.6663 21.0836 14.8464 19.7461C15.0326 18.3641 15.4281 17.2263 16.3298 16.3262C17.2313 15.4263 18.371 15.0324 19.7546 14.8467C21.0942 14.667 22.8024 14.667 24.9431 14.667H25.0916ZM26.3337 37.334C26.8859 37.3342 27.3337 37.7818 27.3337 38.334C27.3336 38.886 26.8858 39.3338 26.3337 39.334H23.6667C23.1146 39.334 22.6669 38.8861 22.6667 38.334C22.6667 37.7817 23.1145 37.334 23.6667 37.334H26.3337ZM34.0613 22.334C35.1092 22.334 35.9835 22.3334 36.6775 22.415C37.4013 22.5002 38.0806 22.6882 38.6414 23.1787C39.0527 23.5387 39.2939 23.9683 39.4373 24.4346C39.6144 24.3205 39.7828 24.2195 39.9412 24.1357C40.4355 23.8744 41.1141 23.6249 41.8308 23.9355C42.5884 24.2641 42.8273 24.9666 42.9167 25.5195C43.0006 26.0384 42.9999 26.7186 42.9998 27.4648V28.5361C42.9999 29.2823 43.0006 29.9617 42.9167 30.4805C42.8273 31.0335 42.5886 31.7358 41.8308 32.0645C41.1141 32.3753 40.4356 32.1265 39.9412 31.8652C39.7828 31.7815 39.6144 31.6804 39.4373 31.5664C39.2939 32.0327 39.0527 32.4613 38.6414 32.8213C38.0805 33.312 37.4014 33.4998 36.6775 33.585C35.9835 33.6666 35.1092 33.667 34.0613 33.667H33.2722C32.2243 33.667 31.35 33.6666 30.656 33.585C29.9321 33.4998 29.253 33.312 28.6921 32.8213C28.112 32.3136 27.87 31.6689 27.7634 30.9756C27.6663 30.3434 27.6667 29.5567 27.6667 28.6572V27.3438C27.6667 26.444 27.6663 25.6567 27.7634 25.0244C27.87 24.3312 28.1121 23.6863 28.6921 23.1787C29.2529 22.6882 29.9322 22.5002 30.656 22.415C31.35 22.3334 32.2243 22.334 33.2722 22.334H34.0613ZM40.8757 25.9043C40.5843 26.0584 40.2115 26.3161 39.6658 26.708C39.6672 26.9127 39.6668 27.1251 39.6667 27.3438V28.6572C39.6668 28.8757 39.6672 29.0875 39.6658 29.292C40.2117 29.684 40.5842 29.9426 40.8757 30.0967C40.9012 30.1102 40.9246 30.1223 40.946 30.1328C40.9966 29.7934 40.9998 29.2849 40.9998 28.4561V27.5439C40.9998 26.7154 40.9966 26.2075 40.946 25.8682C40.9246 25.8786 40.9012 25.8908 40.8757 25.9043ZM25.0173 16.667C23.9266 16.667 22.9928 16.6674 22.1843 16.6865C22.2756 17.2211 22.3473 17.5983 22.4333 17.8916C22.5353 18.2391 22.6263 18.3522 22.697 18.415C22.7749 18.4841 22.8965 18.5597 23.2488 18.6084C23.6345 18.6617 24.1612 18.667 24.9998 18.667C25.8385 18.667 26.3659 18.6617 26.7517 18.6084C27.1035 18.5597 27.2246 18.4841 27.3025 18.415C27.3733 18.3522 27.4641 18.2394 27.5662 17.8916C27.6523 17.5981 27.7248 17.2207 27.8162 16.6855C27.0156 16.6672 26.0927 16.667 25.0173 16.667Z" fill="#FFAF15"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 4.2 KiB | 
| @ -0,0 +1,7 @@ | |||||||
|  | <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <path d="M9.50024 2.25C6.87689 2.25 4.75024 4.37665 4.75024 7C4.75024 9.62335 6.87689 11.75 9.50024 11.75C12.1236 11.75 14.2502 9.62335 14.2502 7C14.2502 4.37665 12.1236 2.25 9.50024 2.25Z" fill="#2B353E"/> | ||||||
|  |     <path d="M14.5577 14.6547C11.4622 12.7818 7.53812 12.7818 4.4426 14.6547C4.3457 14.7133 4.22477 14.7826 4.087 14.8616C3.47435 15.2126 2.52862 15.7545 1.88204 16.3976C1.47697 16.8005 1.08323 17.3398 1.01154 18.0063C0.935065 18.7171 1.24178 19.3796 1.84219 19.9609C2.85901 20.9452 4.09536 21.75 5.70341 21.75L13.2969 21.75C14.905 21.75 16.1413 20.9452 17.1581 19.9609C17.7585 19.3796 18.0653 18.7171 17.9888 18.0063C17.9171 17.3398 17.5234 16.8005 17.1183 16.3976C16.4717 15.7545 15.5264 15.2128 14.9137 14.8618C14.7759 14.7828 14.6547 14.7134 14.5577 14.6547Z" fill="#2B353E"/> | ||||||
|  |     <path d="M17 4C16.4477 4 16 4.44772 16 5C16 5.55228 16.4477 6 17 6L22 6C22.5523 6 23 5.55229 23 5C23 4.44772 22.5523 4 22 4L17 4Z" fill="#2B353E"/> | ||||||
|  |     <path d="M17 7C16.4477 7 16 7.44771 16 8C16 8.55228 16.4477 9 17 9L22 9C22.5523 9 23 8.55229 23 8C23 7.44772 22.5523 7 22 7L17 7Z" fill="#2B353E"/> | ||||||
|  |     <path d="M20 10C19.4477 10 19 10.4477 19 11C19 11.5523 19.4477 12 20 12H22C22.5523 12 23 11.5523 23 11C23 10.4477 22.5523 10 22 10L20 10Z" fill="#2B353E"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 1.3 KiB | 
| @ -0,0 +1,6 @@ | |||||||
|  | <svg width="58" height="58" viewBox="0 0 58 58" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <rect width="58" height="58" rx="12" fill="#18C273" fill-opacity="0.1"/> | ||||||
|  |     <path d="M41.1495 17.9128C39.3937 14.8205 35.478 13.7371 32.4046 15.5414L19.1836 23.3031C16.1285 25.0967 15.1039 29.0427 16.8501 32.1181C17.9052 33.9763 19.7404 35.1109 21.7077 35.3258C22.4214 35.4038 23.0635 34.8922 23.142 34.1831C23.2205 33.4741 22.7056 32.836 21.9919 32.7581C20.838 32.632 19.7493 31.9667 19.1146 30.8489C18.0504 28.9745 18.6936 26.5913 20.5063 25.5271L25.9992 22.3024L27.2315 24.3677C27.5977 24.9813 28.3952 25.1838 29.0128 24.8201C29.6304 24.4563 29.8342 23.664 29.4681 23.0503L28.2376 20.9882L33.7272 17.7654C35.5217 16.712 37.8303 17.3246 38.885 19.182C39.7365 20.6817 39.4968 22.5208 38.4222 23.7388C37.949 24.2752 38.003 25.0913 38.5429 25.5615C39.0829 26.0317 39.9042 25.978 40.3775 25.4415C42.163 23.4176 42.5559 20.3897 41.1495 17.9128Z" fill="#18C273"/> | ||||||
|  |     <path d="M40.0494 28.9364C40.0195 29.1249 39.8361 29.2309 39.4691 29.4427L25.5233 37.4943C25.1563 37.7062 24.9728 37.8121 24.7947 37.7437C24.6166 37.6753 24.555 37.4856 24.4318 37.1062C23.1479 33.1515 24.7511 28.7034 28.4965 26.5409C32.242 24.3785 36.8958 25.2142 39.6787 28.3034C39.9457 28.5998 40.0792 28.748 40.0494 28.9364Z" fill="#18C273"/> | ||||||
|  |     <path d="M41.1978 30.9253C41.0197 30.8569 40.8363 30.9628 40.4694 31.1746L26.523 39.2265C26.1561 39.4384 25.9727 39.5443 25.9428 39.7327C25.913 39.9212 26.0464 40.0694 26.3133 40.3657C29.0962 43.4559 33.7506 44.2921 37.4965 42.1294C41.2425 39.9667 42.8455 35.5177 41.5608 31.5626C41.4376 31.1833 41.376 30.9936 41.1978 30.9253Z" fill="#18C273"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 1.6 KiB | 
| @ -0,0 +1,4 @@ | |||||||
|  | <svg width="58" height="58" viewBox="0 0 58 58" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <rect width="58" height="58" rx="12" fill="#ED1C2B" fill-opacity="0.1"/> | ||||||
|  |     <path fill-rule="evenodd" clip-rule="evenodd" d="M18.3333 14.333C17.597 14.333 17 14.93 17 15.6663C17 16.4027 17.597 16.9997 18.3333 16.9997H18.6667V19.8179C18.6667 23.8629 21.0606 27.357 24.5286 28.9997C21.0606 30.6424 18.6667 34.1365 18.6667 38.1815V40.9997H18.3333C17.597 40.9997 17 41.5966 17 42.333C17 43.0694 17.597 43.6663 18.3333 43.6663H39.6667C40.403 43.6663 41 43.0694 41 42.333C41 41.5966 40.403 40.9997 39.6667 40.9997H39.3333V38.1815C39.3333 34.1365 36.9394 30.6424 33.4714 28.9997C36.9394 27.357 39.3333 23.8629 39.3333 19.8179V16.9997H39.6667C40.403 16.9997 41 16.4027 41 15.6663C41 14.93 40.403 14.333 39.6667 14.333H18.3333ZM36.75 16.9997H21.25V19.8179C21.25 24.0353 24.7198 27.4542 29 27.4542C33.2802 27.4542 36.75 24.0353 36.75 19.8179V16.9997Z" fill="#ED1C2B"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 967 B | 
| @ -0,0 +1,3 @@ | |||||||
|  | <svg width="22" height="18" viewBox="0 0 22 18" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <path fill-rule="evenodd" clip-rule="evenodd" d="M9.07204 8.11243e-07L7.92797 8.11243e-07C6.33935 -2.75213e-05 5.04614 -5.05894e-05 4.02631 0.137062C2.96232 0.280112 2.04736 0.588692 1.31802 1.31802C0.588692 2.04736 0.280112 2.96232 0.137062 4.02631C-5.05894e-05 5.04614 -2.75213e-05 6.33928 8.11243e-07 7.9279L8.11243e-07 10.072C-2.75213e-05 11.6607 -5.05894e-05 12.9539 0.137062 13.9737C0.280112 15.0377 0.588692 15.9527 1.31802 16.682C2.04736 17.4113 2.96232 17.7199 4.02631 17.8629C5.04616 18.0001 6.33933 18 7.928 18H9.07201C10.6607 18 11.9538 18.0001 12.9737 17.8629C14.0377 17.7199 14.9527 17.4113 15.682 16.682C16.3875 15.9764 16.6993 15.0972 16.8484 14.0773C17.3063 14.4374 17.7206 14.7408 18.0944 14.9666C18.7947 15.3896 19.6822 15.7439 20.5929 15.2925C21.4895 14.848 21.7673 13.9371 21.8829 13.1198C21.9991 12.2982 21.9991 11.1926 21.999 9.86445V8.13821C21.9991 6.81011 21.9991 5.70445 21.8829 4.88282C21.7673 4.06553 21.4895 3.15463 20.5929 2.71019C19.6822 2.25874 18.7947 2.61301 18.0944 3.03609C17.7207 3.26185 17.3066 3.56515 16.8488 3.92504C16.6998 2.90419 16.3881 2.02411 15.682 1.31802C14.9527 0.588692 14.0377 0.280112 12.9737 0.137062C11.9539 -5.05894e-05 10.6607 -2.75213e-05 9.07204 8.11243e-07ZM16.9943 6.3825C17 6.86282 17 7.37768 17 7.928V10.072C17 10.6233 17 11.1391 16.9943 11.6201C17.9515 12.4089 18.6126 12.943 19.1285 13.2547C19.3999 13.4186 19.5637 13.4778 19.6531 13.4955C19.6728 13.4994 19.6863 13.5007 19.6942 13.5012C19.6982 13.5014 19.7027 13.5013 19.7027 13.5013L19.7047 13.5005C19.7064 13.4997 19.7078 13.4988 19.7087 13.4982C19.7101 13.4972 19.7108 13.4966 19.7108 13.4966C19.7136 13.4936 19.7286 13.4774 19.7499 13.4358C19.7977 13.3422 19.8567 13.1641 19.9026 12.8397C19.9966 12.1754 19.999 11.2135 19.999 9.78325V8.21941C19.999 6.78915 19.9966 5.82725 19.9026 5.16294C19.8567 4.83855 19.7977 4.66044 19.7499 4.56691C19.7286 4.52522 19.714 4.50949 19.7112 4.50653C19.7112 4.50653 19.709 4.50427 19.7047 4.50211L19.7032 4.50139C19.7032 4.50139 19.6982 4.5013 19.6942 4.5015C19.6863 4.50191 19.6728 4.50322 19.6531 4.50713C19.5637 4.52483 19.3999 4.58402 19.1285 4.74796C18.6126 5.05966 17.9515 5.59379 16.9943 6.3825ZM9 5C9 4.44772 9.44772 4 10 4L12 4C12.5523 4 13 4.44772 13 5C13 5.55229 12.5523 6 12 6L10 6C9.44772 6 9 5.55229 9 5Z" fill="white"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 2.3 KiB | 
| @ -0,0 +1,5 @@ | |||||||
|  | <svg width="38" height="38" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <circle cx="19" cy="19" r="16" fill="#18C273"/> | ||||||
|  |     <circle cx="19" cy="19" r="17.5" stroke="#18C273" stroke-opacity="0.3" stroke-width="3"/> | ||||||
|  |     <path fill-rule="evenodd" clip-rule="evenodd" d="M18.554 12.25H17.696C16.5045 12.25 15.5346 12.25 14.7697 12.3528C13.9717 12.4601 13.2855 12.6915 12.7385 13.2385C12.1915 13.7855 11.9601 14.4717 11.8528 15.2697C11.75 16.0346 11.75 17.0045 11.75 18.1959V19.804C11.75 20.9955 11.75 21.9654 11.8528 22.7303C11.9601 23.5283 12.1915 24.2145 12.7385 24.7615C13.2855 25.3085 13.9717 25.5399 14.7697 25.6472C15.5346 25.75 16.5045 25.75 17.696 25.75H18.554C19.7455 25.75 20.7154 25.75 21.4803 25.6472C22.2783 25.5399 22.9645 25.3085 23.5115 24.7615C24.0406 24.2323 24.2745 23.5729 24.3863 22.808C24.7298 23.078 25.0404 23.3056 25.3208 23.4749C25.846 23.7922 26.5117 24.0579 27.1947 23.7194C27.8671 23.386 28.0755 22.7028 28.1622 22.0899C28.2493 21.4737 28.2493 20.6444 28.2493 19.6483V18.3537C28.2493 17.3576 28.2493 16.5283 28.1622 15.9121C28.0755 15.2992 27.8671 14.616 27.1947 14.2826C26.5117 13.9441 25.846 14.2098 25.3208 14.5271C25.0405 14.6964 24.7299 14.9239 24.3866 15.1938C24.2748 14.4281 24.0411 13.7681 23.5115 13.2385C22.9645 12.6915 22.2783 12.4601 21.4803 12.3528C20.7154 12.25 19.7455 12.25 18.554 12.25ZM24.4957 17.0369C24.5 17.3971 24.5 17.7833 24.5 18.196V19.804C24.5 20.2175 24.5 20.6043 24.4957 20.9651C25.2136 21.5566 25.7094 21.9572 26.0964 22.191C26.2999 22.314 26.4227 22.3584 26.4898 22.3717C26.5046 22.3746 26.5147 22.3756 26.5207 22.3759C26.5237 22.376 26.527 22.376 26.527 22.376L26.5285 22.3754C26.5298 22.3748 26.5308 22.3741 26.5316 22.3736C26.5326 22.3729 26.5331 22.3724 26.5331 22.3724C26.5352 22.3702 26.5464 22.3581 26.5624 22.3268C26.5983 22.2567 26.6425 22.1231 26.6769 21.8798C26.7474 21.3816 26.7493 20.6601 26.7493 19.5874V18.4146C26.7493 17.3419 26.7474 16.6204 26.6769 16.1222C26.6425 15.8789 26.5983 15.7453 26.5624 15.6752C26.5464 15.6439 26.5355 15.6321 26.5334 15.6299C26.5334 15.6299 26.5318 15.6282 26.5285 15.6266L26.5274 15.626C26.5274 15.626 26.5237 15.626 26.5207 15.6261C26.5147 15.6264 26.5046 15.6274 26.4898 15.6303C26.4227 15.6436 26.2999 15.688 26.0964 15.811C25.7094 16.0447 25.2136 16.4453 24.4957 17.0369ZM18.5 16C18.5 15.5858 18.8358 15.25 19.25 15.25L20.75 15.25C21.1642 15.25 21.5 15.5858 21.5 16C21.5 16.4142 21.1642 16.75 20.75 16.75L19.25 16.75C18.8358 16.75 18.5 16.4142 18.5 16Z" fill="white"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 2.5 KiB | 
| @ -0,0 +1,5 @@ | |||||||
|  | <svg width="58" height="58" viewBox="0 0 58 58" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <rect width="58" height="58" rx="12" fill="#45A2F8" fill-opacity="0.08"/> | ||||||
|  |     <path d="M25 18.667C25 16.4579 26.7909 14.667 29 14.667C31.2091 14.667 33 16.4579 33 18.667C33 20.0974 32.2492 21.3525 31.1201 22.0595C35.0334 22.6144 37.412 26.9667 35.8466 30.8113C35.5059 31.6479 34.7071 32.2225 33.7892 32.2225H33.1331L32.0293 36.8699C31.692 38.29 30.4623 39.3337 29 39.3337C27.5378 39.3337 26.308 38.29 25.9707 36.8699L24.8669 32.2225H24.2108C23.2929 32.2225 22.4941 31.6479 22.1534 30.8113C20.588 26.9667 22.9666 22.6144 26.8799 22.0595C25.7508 21.3525 25 20.0974 25 18.667Z" fill="#0B85F7"/> | ||||||
|  |     <path d="M21.3632 35.1591C22.0032 34.7949 22.2267 33.9808 21.8625 33.3408C21.4983 32.7008 20.6842 32.4773 20.0442 32.8415C18.4192 33.7663 17 35.1905 17 37.0881C17 39.2797 18.8723 40.8389 20.8998 41.7726C23.0416 42.759 25.9068 43.3337 29 43.3337C32.0932 43.3337 34.9584 42.759 37.1002 41.7726C39.1277 40.8389 41 39.2797 41 37.0881C41 35.1905 39.5808 33.7663 37.9558 32.8415C37.3158 32.4773 36.5017 32.7008 36.1375 33.3408C35.7733 33.9808 35.9968 34.7949 36.6368 35.1591C37.9767 35.9216 38.3333 36.6461 38.3333 37.0881C38.3333 37.6095 37.8178 38.5063 35.9847 39.3505C34.266 40.142 31.7978 40.667 29 40.667C26.2022 40.667 23.734 40.142 22.0153 39.3505C20.1822 38.5063 19.6667 37.6095 19.6667 37.0881C19.6667 36.6461 20.0234 35.9216 21.3632 35.1591Z" fill="#0B85F7"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 1.4 KiB | 
| @ -1,17 +1,383 @@ | |||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/date_util.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/loading_utils.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/features/book_appointments/book_appointments_repo.dart'; | import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/models/timeslots.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; | ||||||
| import 'package:hmg_patient_app_new/services/error_handler_service.dart'; | import 'package:hmg_patient_app_new/services/error_handler_service.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/services/navigation_service.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; | ||||||
| 
 | 
 | ||||||
| class BookAppointmentsViewModel extends ChangeNotifier { | class BookAppointmentsViewModel extends ChangeNotifier { | ||||||
|   int selectedTabIndex = 0; |   int selectedTabIndex = 0; | ||||||
| 
 | 
 | ||||||
|  |   bool isClinicsListLoading = false; | ||||||
|  |   bool isDoctorsListLoading = false; | ||||||
|  |   bool isDoctorProfileLoading = false; | ||||||
|  |   bool isDoctorSearchByNameStarted = false; | ||||||
|  | 
 | ||||||
|  |   int initialSlotDuration = 0; | ||||||
|  | 
 | ||||||
|  |   List<GetClinicsListResponseModel> clinicsList = []; | ||||||
|  |   List<GetClinicsListResponseModel> _filteredClinicsList = []; | ||||||
|  | 
 | ||||||
|  |   List<GetClinicsListResponseModel> get filteredClinicsList => _filteredClinicsList; | ||||||
|  | 
 | ||||||
|  |   List<DoctorsListResponseModel> doctorsList = []; | ||||||
|  | 
 | ||||||
|  |   GetClinicsListResponseModel selectedClinic = GetClinicsListResponseModel(); | ||||||
|  |   DoctorsListResponseModel selectedDoctor = DoctorsListResponseModel(); | ||||||
|  | 
 | ||||||
|  |   late DoctorsProfileResponseModel doctorsProfileResponseModel; | ||||||
|  | 
 | ||||||
|  |   List<FreeSlot> slotsList = []; | ||||||
|  |   List<TimeSlot> docFreeSlots = []; | ||||||
|  |   List<TimeSlot> dayEvents = []; | ||||||
|  |   List<TimeSlot> nextDayEvents = []; | ||||||
|  | 
 | ||||||
|  |   String selectedAppointmentDate = ""; | ||||||
|  |   String selectedAppointmentTime = ""; | ||||||
|  | 
 | ||||||
|  |   dynamic freeSlotsResponse; | ||||||
|  | 
 | ||||||
|   BookAppointmentsRepo bookAppointmentsRepo; |   BookAppointmentsRepo bookAppointmentsRepo; | ||||||
|   ErrorHandlerService errorHandlerService; |   ErrorHandlerService errorHandlerService; | ||||||
|  |   final NavigationService navigationService; | ||||||
|  |   MyAppointmentsViewModel myAppointmentsViewModel; | ||||||
|  | 
 | ||||||
|  |   late AppState _appState; | ||||||
|  | 
 | ||||||
|  |   BookAppointmentsViewModel({required this.bookAppointmentsRepo, required this.errorHandlerService, required this.navigationService, required this.myAppointmentsViewModel}); | ||||||
|  | 
 | ||||||
|  |   void initializeFilteredList() { | ||||||
|  |     _filteredClinicsList = List.from(clinicsList); | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void filterClinics(String? query) { | ||||||
|  |     if (query!.isEmpty) { | ||||||
|  |       _filteredClinicsList = List.from(clinicsList); | ||||||
|  |     } else { | ||||||
|  |       _filteredClinicsList = clinicsList.where((clinic) => clinic.clinicDescription?.toLowerCase().contains(query!.toLowerCase()) ?? false).toList(); | ||||||
|  |     } | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   initBookAppointmentViewModel() { | ||||||
|  |     _appState = getIt<AppState>(); | ||||||
|  |     isClinicsListLoading = true; | ||||||
|  |     isDoctorsListLoading = true; | ||||||
|  |     isDoctorProfileLoading = true; | ||||||
|  |     clinicsList.clear(); | ||||||
|  |     doctorsList.clear(); | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setIsDoctorsListLoading(bool value) { | ||||||
|  |     if (value) { | ||||||
|  |       doctorsList.clear(); | ||||||
|  |     } | ||||||
|  |     isDoctorsListLoading = value; | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setIsClinicsListLoading(bool value) { | ||||||
|  |     if (value) { | ||||||
|  |       clinicsList.clear(); | ||||||
|  |     } | ||||||
|  |     isClinicsListLoading = value; | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setSelectedClinic(GetClinicsListResponseModel clinic) { | ||||||
|  |     selectedClinic = clinic; | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setSelectedDoctor(DoctorsListResponseModel doctor) { | ||||||
|  |     selectedDoctor = doctor; | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   BookAppointmentsViewModel({required this.bookAppointmentsRepo, required this.errorHandlerService}); |   setIsDoctorProfileLoading(bool value) { | ||||||
|  |     isDoctorProfileLoading = value; | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setIsDoctorSearchByNameStarted(bool value) { | ||||||
|  |     isDoctorSearchByNameStarted = value; | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setDoctorsProfile(DoctorsProfileResponseModel profile) { | ||||||
|  |     doctorsProfileResponseModel = profile; | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setSelectedAppointmentDateTime(String date, String time) { | ||||||
|  |     selectedAppointmentDate = date; | ||||||
|  |     selectedAppointmentTime = time; | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   void onTabChanged(int index) { |   void onTabChanged(int index) { | ||||||
|     selectedTabIndex = index; |     selectedTabIndex = index; | ||||||
|     notifyListeners(); |     notifyListeners(); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> getClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||||
|  |     final result = await bookAppointmentsRepo.getClinics(); | ||||||
|  | 
 | ||||||
|  |     result.fold( | ||||||
|  |       (failure) async => await errorHandlerService.handleError(failure: failure), | ||||||
|  |       (apiResponse) { | ||||||
|  |         if (apiResponse.messageStatus == 2) { | ||||||
|  |           // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); | ||||||
|  |         } else if (apiResponse.messageStatus == 1) { | ||||||
|  |           clinicsList = apiResponse.data!; | ||||||
|  |           isClinicsListLoading = false; | ||||||
|  |           initializeFilteredList(); | ||||||
|  |           notifyListeners(); | ||||||
|  |           if (onSuccess != null) { | ||||||
|  |             onSuccess(apiResponse); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   //TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName | ||||||
|  |   Future<void> getDoctorsList( | ||||||
|  |       {int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||||
|  |     doctorsList.clear(); | ||||||
|  |     final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName); | ||||||
|  | 
 | ||||||
|  |     result.fold( | ||||||
|  |       (failure) async { | ||||||
|  |         onError!("No doctors found for the search criteria".needTranslation); | ||||||
|  |       }, | ||||||
|  |       (apiResponse) { | ||||||
|  |         if (apiResponse.messageStatus == 2) { | ||||||
|  |           // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); | ||||||
|  |         } else if (apiResponse.messageStatus == 1) { | ||||||
|  |           doctorsList = apiResponse.data!; | ||||||
|  |           isDoctorsListLoading = false; | ||||||
|  |           initializeFilteredList(); | ||||||
|  |           notifyListeners(); | ||||||
|  |           if (onSuccess != null) { | ||||||
|  |             onSuccess(apiResponse); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   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); | ||||||
|  | 
 | ||||||
|  |     result.fold( | ||||||
|  |       (failure) async {}, | ||||||
|  |       (apiResponse) { | ||||||
|  |         if (apiResponse.messageStatus == 2) { | ||||||
|  |           onError!(apiResponse.errorMessage ?? "Unknown error occurred"); | ||||||
|  |           // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); | ||||||
|  |         } else if (apiResponse.messageStatus == 1) { | ||||||
|  |           doctorsProfileResponseModel = apiResponse.data!; | ||||||
|  |           notifyListeners(); | ||||||
|  |           if (onSuccess != null) { | ||||||
|  |             onSuccess(apiResponse); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   //TODO: Handle the cases for LiveCare Schedule | ||||||
|  |   Future<void> getDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||||
|  |     docFreeSlots.clear(); | ||||||
|  |     DateTime date; | ||||||
|  |     final DateFormat formatter = DateFormat('HH:mm'); | ||||||
|  |     final DateFormat dateFormatter = DateFormat('yyyy-MM-dd'); | ||||||
|  |     Map<DateTime, List> _eventsParsed; | ||||||
|  | 
 | ||||||
|  |     final result = await bookAppointmentsRepo.getDoctorFreeSlots(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare, onError: onError); | ||||||
|  | 
 | ||||||
|  |     result.fold( | ||||||
|  |       (failure) async { | ||||||
|  |         print(failure); | ||||||
|  |       }, | ||||||
|  |       (apiResponse) { | ||||||
|  |         if (apiResponse.messageStatus == 2) { | ||||||
|  |           onError!(apiResponse.errorMessage ?? "Unknown error occurred"); | ||||||
|  |           // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); | ||||||
|  |         } else if (apiResponse.messageStatus == 1) { | ||||||
|  |           if (apiResponse.data['FreeTimeSlots'] == null || apiResponse.data['FreeTimeSlots'].isEmpty) { | ||||||
|  |             onError!("No free slots available".tr()); | ||||||
|  |             return; | ||||||
|  |           } | ||||||
|  |           initialSlotDuration = apiResponse.data["InitialSlotDuration"]; | ||||||
|  |           freeSlotsResponse = apiResponse.data['FreeTimeSlots']; | ||||||
|  |           freeSlotsResponse.forEach((element) { | ||||||
|  |             // date = (isLiveCareSchedule != null && isLiveCareSchedule) | ||||||
|  |             //     ? DateUtil.convertStringToDate(element) | ||||||
|  |             //     : | ||||||
|  |             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)); | ||||||
|  |           }); | ||||||
|  | 
 | ||||||
|  |           notifyListeners(); | ||||||
|  |           if (onSuccess != null) { | ||||||
|  |             onSuccess(apiResponse); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||||
|  |     final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel); | ||||||
|  | 
 | ||||||
|  |     result.fold( | ||||||
|  |       (failure) async => await errorHandlerService.handleError(failure: failure), | ||||||
|  |       (apiResponse) { | ||||||
|  |         if (apiResponse.messageStatus == 2) { | ||||||
|  |           onError!(apiResponse.errorMessage!); | ||||||
|  |           // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); | ||||||
|  |         } else if (apiResponse.messageStatus == 1) { | ||||||
|  |           notifyListeners(); | ||||||
|  |           if (onSuccess != null) { | ||||||
|  |             onSuccess(apiResponse); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   //TODO: Handle the cases for LiveCare Schedule, Dental & Laser Clinics | ||||||
|  |   Future<void> insertSpecificAppointment( | ||||||
|  |       { | ||||||
|  |       // required int docID, | ||||||
|  |       // required int clinicID, | ||||||
|  |       // required int projectID, | ||||||
|  |       // required String selectedTime, | ||||||
|  |       // required String selectedDate, | ||||||
|  |       // required int initialSlotDuration, | ||||||
|  |       // required int genderID, | ||||||
|  |       // required int userAge, | ||||||
|  |       String? procedureID, | ||||||
|  |       num? testTypeEnum, | ||||||
|  |       num? testProcedureEnum, | ||||||
|  |       int? invoiceNumber, | ||||||
|  |       int? lineItemNo, | ||||||
|  |       String? invoiceNoVP, | ||||||
|  |       Function(dynamic p1)? onSuccess, | ||||||
|  |       Function(dynamic p1)? onError}) async { | ||||||
|  |     _appState = getIt<AppState>(); | ||||||
|  |     final result = await bookAppointmentsRepo.insertSpecificAppointment( | ||||||
|  |         docID: selectedDoctor.doctorID!, | ||||||
|  |         clinicID: selectedDoctor.clinicID!, | ||||||
|  |         projectID: selectedDoctor.projectID!, | ||||||
|  |         selectedDate: selectedAppointmentDate, | ||||||
|  |         selectedTime: selectedAppointmentTime, | ||||||
|  |         initialSlotDuration: initialSlotDuration, | ||||||
|  |         genderID: _appState.getAuthenticatedUser()!.gender!, | ||||||
|  |         userAge: _appState.getAuthenticatedUser()!.age!, | ||||||
|  |         onError: onError); | ||||||
|  | 
 | ||||||
|  |     result.fold( | ||||||
|  |       (failure) async { | ||||||
|  |         print(failure); | ||||||
|  |       }, | ||||||
|  |       (apiResponse) { | ||||||
|  |         if (apiResponse.messageStatus == 2) { | ||||||
|  |           // onError!(apiResponse); | ||||||
|  |           LoadingUtils.hideFullScreenLoader(); | ||||||
|  |           showCommonBottomSheetWithoutHeight( | ||||||
|  |             title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), | ||||||
|  |             navigationService.navigatorKey.currentContext!, | ||||||
|  |             child: Utils.getWarningWidget( | ||||||
|  |                 loadingText: apiResponse.data["ErrorEndUserMessage"], | ||||||
|  |                 isShowActionButtons: true, | ||||||
|  |                 onCancelTap: () { | ||||||
|  |                   navigationService.pop(); | ||||||
|  |                 }, | ||||||
|  |                 onConfirmTap: () async { | ||||||
|  |                   navigationService.pop(); | ||||||
|  |                   PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel( | ||||||
|  |                     appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'], | ||||||
|  |                     clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'], | ||||||
|  |                     projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'], | ||||||
|  |                     endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'], | ||||||
|  |                     startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'], | ||||||
|  |                     doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'], | ||||||
|  |                     isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'], | ||||||
|  |                     originalClinicID: 0, | ||||||
|  |                     originalProjectID: 0, | ||||||
|  |                     appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'], | ||||||
|  |                   ); | ||||||
|  | 
 | ||||||
|  |                   showCommonBottomSheet(navigationService.navigatorKey.currentContext!, | ||||||
|  |                       child: Utils.getLoadingWidget(loadingText: "Cancelling your previous appointment....".needTranslation), | ||||||
|  |                       callBackFunc: (str) {}, | ||||||
|  |                       title: "", | ||||||
|  |                       height: ResponsiveExtension.screenHeight * 0.3, | ||||||
|  |                       isCloseButtonVisible: false, | ||||||
|  |                       isDismissible: false, | ||||||
|  |                       isFullScreen: false); | ||||||
|  |                   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); | ||||||
|  |                     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()); | ||||||
|  |                             await Future.delayed(Duration(milliseconds: 4000)).then((value) { | ||||||
|  |                               LoadingUtils.hideFullScreenLoader(); | ||||||
|  |                               Navigator.pushAndRemoveUntil( | ||||||
|  |                                   navigationService.navigatorKey.currentContext!, | ||||||
|  |                                   FadePage( | ||||||
|  |                                     page: LandingNavigation(), | ||||||
|  |                                   ), | ||||||
|  |                                   (r) => false); | ||||||
|  |                             }); | ||||||
|  |                           }); | ||||||
|  |                         }); | ||||||
|  |                   }); | ||||||
|  |                 }), | ||||||
|  |             callBackFunc: () {}, | ||||||
|  |             isFullScreen: false, | ||||||
|  |             isCloseButtonVisible: true, | ||||||
|  |           ); | ||||||
|  |         } else if (apiResponse.messageStatus == 1) { | ||||||
|  |           if (apiResponse.data == null || apiResponse.data!.isEmpty) { | ||||||
|  |             onError!("No free slots available".tr()); | ||||||
|  |             return; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           notifyListeners(); | ||||||
|  |           if (onSuccess != null) { | ||||||
|  |             onSuccess(apiResponse); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -0,0 +1,12 @@ | |||||||
|  | class FreeSlot { | ||||||
|  |   List event; | ||||||
|  |   DateTime slot; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   FreeSlot(this.slot, this.event); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() { | ||||||
|  |     return '{ ${this.slot}, ${this.event} }'; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,196 @@ | |||||||
|  | class DoctorsProfileResponseModel { | ||||||
|  |   int? doctorID; | ||||||
|  |   String? doctorName; | ||||||
|  |   dynamic doctorNameN; | ||||||
|  |   int? clinicID; | ||||||
|  |   String? clinicDescription; | ||||||
|  |   dynamic clinicDescriptionN; | ||||||
|  |   dynamic licenseExpiry; | ||||||
|  |   int? employmentType; | ||||||
|  |   dynamic setupID; | ||||||
|  |   int? projectID; | ||||||
|  |   String? projectName; | ||||||
|  |   String? nationalityID; | ||||||
|  |   String? nationalityName; | ||||||
|  |   dynamic nationalityNameN; | ||||||
|  |   int? gender; | ||||||
|  |   String? genderDescription; | ||||||
|  |   dynamic genderDescriptionN; | ||||||
|  |   dynamic doctorTitle; | ||||||
|  |   dynamic projectNameN; | ||||||
|  |   bool? isAllowWaitList; | ||||||
|  |   String? titleDescription; | ||||||
|  |   dynamic titleDescriptionN; | ||||||
|  |   dynamic isRegistered; | ||||||
|  |   dynamic isDoctorDummy; | ||||||
|  |   bool? isActive; | ||||||
|  |   dynamic isDoctorAppointmentDisplayed; | ||||||
|  |   bool? doctorClinicActive; | ||||||
|  |   dynamic isbookingAllowed; | ||||||
|  |   String? doctorCases; | ||||||
|  |   dynamic doctorPicture; | ||||||
|  |   String? doctorProfileInfo; | ||||||
|  |   List<String>? specialty; | ||||||
|  |   num? actualDoctorRate; | ||||||
|  |   String? consultationFee; | ||||||
|  |   num? decimalDoctorRate; | ||||||
|  |   String? doctorImageURL; | ||||||
|  |   String? doctorMobileNumber; | ||||||
|  |   num? doctorRate; | ||||||
|  |   num? doctorStarsRate; | ||||||
|  |   String? doctorTitleForProfile; | ||||||
|  |   bool? isAppointmentAllowed; | ||||||
|  |   bool? isDoctorHasPrePostImages; | ||||||
|  |   String? nationalityFlagURL; | ||||||
|  |   num? noOfPatientsRate; | ||||||
|  |   String? qR; | ||||||
|  |   int? serviceID; | ||||||
|  | 
 | ||||||
|  |   DoctorsProfileResponseModel( | ||||||
|  |       {this.doctorID, | ||||||
|  |       this.doctorName, | ||||||
|  |       this.doctorNameN, | ||||||
|  |       this.clinicID, | ||||||
|  |       this.clinicDescription, | ||||||
|  |       this.clinicDescriptionN, | ||||||
|  |       this.licenseExpiry, | ||||||
|  |       this.employmentType, | ||||||
|  |       this.setupID, | ||||||
|  |       this.projectID, | ||||||
|  |       this.projectName, | ||||||
|  |       this.nationalityID, | ||||||
|  |       this.nationalityName, | ||||||
|  |       this.nationalityNameN, | ||||||
|  |       this.gender, | ||||||
|  |       this.genderDescription, | ||||||
|  |       this.genderDescriptionN, | ||||||
|  |       this.doctorTitle, | ||||||
|  |       this.projectNameN, | ||||||
|  |       this.isAllowWaitList, | ||||||
|  |       this.titleDescription, | ||||||
|  |       this.titleDescriptionN, | ||||||
|  |       this.isRegistered, | ||||||
|  |       this.isDoctorDummy, | ||||||
|  |       this.isActive, | ||||||
|  |       this.isDoctorAppointmentDisplayed, | ||||||
|  |       this.doctorClinicActive, | ||||||
|  |       this.isbookingAllowed, | ||||||
|  |       this.doctorCases, | ||||||
|  |       this.doctorPicture, | ||||||
|  |       this.doctorProfileInfo, | ||||||
|  |       this.specialty, | ||||||
|  |       this.actualDoctorRate, | ||||||
|  |       this.consultationFee, | ||||||
|  |       this.decimalDoctorRate, | ||||||
|  |       this.doctorImageURL, | ||||||
|  |       this.doctorMobileNumber, | ||||||
|  |       this.doctorRate, | ||||||
|  |       this.doctorStarsRate, | ||||||
|  |       this.doctorTitleForProfile, | ||||||
|  |       this.isAppointmentAllowed, | ||||||
|  |       this.isDoctorHasPrePostImages, | ||||||
|  |       this.nationalityFlagURL, | ||||||
|  |       this.noOfPatientsRate, | ||||||
|  |       this.qR, | ||||||
|  |       this.serviceID}); | ||||||
|  | 
 | ||||||
|  |   DoctorsProfileResponseModel.fromJson(Map<String, dynamic> json) { | ||||||
|  |     doctorID = json['DoctorID']; | ||||||
|  |     doctorName = json['DoctorName']; | ||||||
|  |     doctorNameN = json['DoctorNameN']; | ||||||
|  |     clinicID = json['ClinicID']; | ||||||
|  |     clinicDescription = json['ClinicDescription']; | ||||||
|  |     clinicDescriptionN = json['ClinicDescriptionN']; | ||||||
|  |     licenseExpiry = json['LicenseExpiry']; | ||||||
|  |     employmentType = json['EmploymentType']; | ||||||
|  |     setupID = json['SetupID']; | ||||||
|  |     projectID = json['ProjectID']; | ||||||
|  |     projectName = json['ProjectName']; | ||||||
|  |     nationalityID = json['NationalityID']; | ||||||
|  |     nationalityName = json['NationalityName']; | ||||||
|  |     nationalityNameN = json['NationalityNameN']; | ||||||
|  |     gender = json['Gender']; | ||||||
|  |     genderDescription = json['Gender_Description']; | ||||||
|  |     genderDescriptionN = json['Gender_DescriptionN']; | ||||||
|  |     doctorTitle = json['DoctorTitle']; | ||||||
|  |     projectNameN = json['ProjectNameN']; | ||||||
|  |     isAllowWaitList = json['IsAllowWaitList']; | ||||||
|  |     titleDescription = json['Title_Description']; | ||||||
|  |     titleDescriptionN = json['Title_DescriptionN']; | ||||||
|  |     isRegistered = json['IsRegistered']; | ||||||
|  |     isDoctorDummy = json['IsDoctorDummy']; | ||||||
|  |     isActive = json['IsActive']; | ||||||
|  |     isDoctorAppointmentDisplayed = json['IsDoctorAppointmentDisplayed']; | ||||||
|  |     doctorClinicActive = json['DoctorClinicActive']; | ||||||
|  |     isbookingAllowed = json['IsbookingAllowed']; | ||||||
|  |     doctorCases = json['DoctorCases']; | ||||||
|  |     doctorPicture = json['DoctorPicture']; | ||||||
|  |     doctorProfileInfo = json['DoctorProfileInfo']; | ||||||
|  |     specialty = json['Specialty'].cast<String>(); | ||||||
|  |     actualDoctorRate = json['ActualDoctorRate']; | ||||||
|  |     consultationFee = json['ConsultationFee']; | ||||||
|  |     decimalDoctorRate = json['DecimalDoctorRate']; | ||||||
|  |     doctorImageURL = json['DoctorImageURL']; | ||||||
|  |     doctorMobileNumber = json['DoctorMobileNumber']; | ||||||
|  |     doctorRate = json['DoctorRate']; | ||||||
|  |     doctorStarsRate = json['DoctorStarsRate']; | ||||||
|  |     doctorTitleForProfile = json['DoctorTitleForProfile']; | ||||||
|  |     isAppointmentAllowed = json['IsAppointmentAllowed']; | ||||||
|  |     isDoctorHasPrePostImages = json['IsDoctorHasPrePostImages']; | ||||||
|  |     nationalityFlagURL = json['NationalityFlagURL']; | ||||||
|  |     noOfPatientsRate = json['NoOfPatientsRate']; | ||||||
|  |     qR = json['QR']; | ||||||
|  |     serviceID = json['ServiceID']; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() { | ||||||
|  |     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||||
|  |     data['DoctorID'] = this.doctorID; | ||||||
|  |     data['DoctorName'] = this.doctorName; | ||||||
|  |     data['DoctorNameN'] = this.doctorNameN; | ||||||
|  |     data['ClinicID'] = this.clinicID; | ||||||
|  |     data['ClinicDescription'] = this.clinicDescription; | ||||||
|  |     data['ClinicDescriptionN'] = this.clinicDescriptionN; | ||||||
|  |     data['LicenseExpiry'] = this.licenseExpiry; | ||||||
|  |     data['EmploymentType'] = this.employmentType; | ||||||
|  |     data['SetupID'] = this.setupID; | ||||||
|  |     data['ProjectID'] = this.projectID; | ||||||
|  |     data['ProjectName'] = this.projectName; | ||||||
|  |     data['NationalityID'] = this.nationalityID; | ||||||
|  |     data['NationalityName'] = this.nationalityName; | ||||||
|  |     data['NationalityNameN'] = this.nationalityNameN; | ||||||
|  |     data['Gender'] = this.gender; | ||||||
|  |     data['Gender_Description'] = this.genderDescription; | ||||||
|  |     data['Gender_DescriptionN'] = this.genderDescriptionN; | ||||||
|  |     data['DoctorTitle'] = this.doctorTitle; | ||||||
|  |     data['ProjectNameN'] = this.projectNameN; | ||||||
|  |     data['IsAllowWaitList'] = this.isAllowWaitList; | ||||||
|  |     data['Title_Description'] = this.titleDescription; | ||||||
|  |     data['Title_DescriptionN'] = this.titleDescriptionN; | ||||||
|  |     data['IsRegistered'] = this.isRegistered; | ||||||
|  |     data['IsDoctorDummy'] = this.isDoctorDummy; | ||||||
|  |     data['IsActive'] = this.isActive; | ||||||
|  |     data['IsDoctorAppointmentDisplayed'] = this.isDoctorAppointmentDisplayed; | ||||||
|  |     data['DoctorClinicActive'] = this.doctorClinicActive; | ||||||
|  |     data['IsbookingAllowed'] = this.isbookingAllowed; | ||||||
|  |     data['DoctorCases'] = this.doctorCases; | ||||||
|  |     data['DoctorPicture'] = this.doctorPicture; | ||||||
|  |     data['DoctorProfileInfo'] = this.doctorProfileInfo; | ||||||
|  |     data['Specialty'] = this.specialty; | ||||||
|  |     data['ActualDoctorRate'] = this.actualDoctorRate; | ||||||
|  |     data['ConsultationFee'] = this.consultationFee; | ||||||
|  |     data['DecimalDoctorRate'] = this.decimalDoctorRate; | ||||||
|  |     data['DoctorImageURL'] = this.doctorImageURL; | ||||||
|  |     data['DoctorMobileNumber'] = this.doctorMobileNumber; | ||||||
|  |     data['DoctorRate'] = this.doctorRate; | ||||||
|  |     data['DoctorStarsRate'] = this.doctorStarsRate; | ||||||
|  |     data['DoctorTitleForProfile'] = this.doctorTitleForProfile; | ||||||
|  |     data['IsAppointmentAllowed'] = this.isAppointmentAllowed; | ||||||
|  |     data['IsDoctorHasPrePostImages'] = this.isDoctorHasPrePostImages; | ||||||
|  |     data['NationalityFlagURL'] = this.nationalityFlagURL; | ||||||
|  |     data['NoOfPatientsRate'] = this.noOfPatientsRate; | ||||||
|  |     data['QR'] = this.qR; | ||||||
|  |     data['ServiceID'] = this.serviceID; | ||||||
|  |     return data; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,252 @@ | |||||||
|  | class DoctorsListResponseModel { | ||||||
|  |   int? clinicID; | ||||||
|  |   String? clinicName; | ||||||
|  |   String? clinicNameN; | ||||||
|  |   String? doctorTitle; | ||||||
|  |   int? iD; | ||||||
|  |   String? name; | ||||||
|  |   int? projectID; | ||||||
|  |   String? projectName; | ||||||
|  |   num? actualDoctorRate; | ||||||
|  |   int? clinicRoomNo; | ||||||
|  |   dynamic date; | ||||||
|  |   dynamic dayName; | ||||||
|  |   num? decimalDoctorRate; | ||||||
|  |   dynamic doctorAvailability; | ||||||
|  |   int? doctorID; | ||||||
|  |   String? doctorImageURL; | ||||||
|  |   String? doctorMobileNumber; | ||||||
|  |   dynamic doctorProfile; | ||||||
|  |   dynamic doctorProfileInfo; | ||||||
|  |   num? doctorRate; | ||||||
|  |   num? doctorStarsRate; | ||||||
|  |   int? employmentType; | ||||||
|  |   int? gender; | ||||||
|  |   String? genderDescription; | ||||||
|  |   int? hISRegionId; | ||||||
|  |   bool? isActive; | ||||||
|  |   bool? isAllowWaitList; | ||||||
|  |   bool? isAppointmentAllowed; | ||||||
|  |   bool? isDoctorAllowVedioCall; | ||||||
|  |   bool? isDoctorDummy; | ||||||
|  |   bool? isDoctorHasPrePostImages; | ||||||
|  |   bool? isHMC; | ||||||
|  |   bool? isHmg; | ||||||
|  |   bool? isLiveCare; | ||||||
|  |   String? latitude; | ||||||
|  |   String? longitude; | ||||||
|  |   String? nationalityFlagURL; | ||||||
|  |   String? nationalityID; | ||||||
|  |   String? nationalityName; | ||||||
|  |   dynamic nearestFreeSlot; | ||||||
|  |   int? noOfFreeSlotsAvailable; | ||||||
|  |   num? noOfPatientsRate; | ||||||
|  |   dynamic originalClinicID; | ||||||
|  |   int? personRate; | ||||||
|  |   num? projectDistanceInKiloMeters; | ||||||
|  |   String? projectNameBottom; | ||||||
|  |   String? projectNameTop; | ||||||
|  |   String? qR; | ||||||
|  |   dynamic qRString; | ||||||
|  |   num? rateNumber; | ||||||
|  |   String? regionName; | ||||||
|  |   dynamic regionNameN; | ||||||
|  |   dynamic serviceID; | ||||||
|  |   String? setupID; | ||||||
|  |   List<String>? speciality; | ||||||
|  |   List<String>? specialityN; | ||||||
|  |   int? transactionType; | ||||||
|  |   int? virtualEmploymentType; | ||||||
|  |   dynamic workingHours; | ||||||
|  |   dynamic vida3Id; | ||||||
|  | 
 | ||||||
|  |   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}); | ||||||
|  | 
 | ||||||
|  |   DoctorsListResponseModel.fromJson(Map<String, dynamic> json) { | ||||||
|  |     clinicID = json['ClinicID']; | ||||||
|  |     clinicName = json['ClinicName']; | ||||||
|  |     clinicNameN = json['ClinicNameN']; | ||||||
|  |     doctorTitle = json['DoctorTitle']; | ||||||
|  |     iD = json['ID']; | ||||||
|  |     name = json['Name']; | ||||||
|  |     projectID = json['ProjectID']; | ||||||
|  |     projectName = json['ProjectName']; | ||||||
|  |     actualDoctorRate = json['ActualDoctorRate']; | ||||||
|  |     clinicRoomNo = json['ClinicRoomNo']; | ||||||
|  |     date = json['Date']; | ||||||
|  |     dayName = json['DayName']; | ||||||
|  |     decimalDoctorRate = json['DecimalDoctorRate']; | ||||||
|  |     doctorAvailability = json['DoctorAvailability']; | ||||||
|  |     doctorID = json['DoctorID']; | ||||||
|  |     doctorImageURL = json['DoctorImageURL']; | ||||||
|  |     doctorMobileNumber = json['DoctorMobileNumber']; | ||||||
|  |     doctorProfile = json['DoctorProfile']; | ||||||
|  |     doctorProfileInfo = json['DoctorProfileInfo']; | ||||||
|  |     doctorRate = json['DoctorRate']; | ||||||
|  |     doctorStarsRate = json['DoctorStarsRate']; | ||||||
|  |     employmentType = json['EmploymentType']; | ||||||
|  |     gender = json['Gender']; | ||||||
|  |     genderDescription = json['GenderDescription']; | ||||||
|  |     hISRegionId = json['HISRegionId']; | ||||||
|  |     isActive = json['IsActive']; | ||||||
|  |     isAllowWaitList = json['IsAllowWaitList']; | ||||||
|  |     isAppointmentAllowed = json['IsAppointmentAllowed']; | ||||||
|  |     isDoctorAllowVedioCall = json['IsDoctorAllowVedioCall']; | ||||||
|  |     isDoctorDummy = json['IsDoctorDummy']; | ||||||
|  |     isDoctorHasPrePostImages = json['IsDoctorHasPrePostImages']; | ||||||
|  |     isHMC = json['IsHMC']; | ||||||
|  |     isHmg = json['IsHmg']; | ||||||
|  |     isLiveCare = json['IsLiveCare']; | ||||||
|  |     latitude = json['Latitude']; | ||||||
|  |     longitude = json['Longitude']; | ||||||
|  |     nationalityFlagURL = json['NationalityFlagURL']; | ||||||
|  |     nationalityID = json['NationalityID']; | ||||||
|  |     nationalityName = json['NationalityName']; | ||||||
|  |     nearestFreeSlot = json['NearestFreeSlot']; | ||||||
|  |     noOfFreeSlotsAvailable = json['NoOfFreeSlotsAvailable']; | ||||||
|  |     noOfPatientsRate = json['NoOfPatientsRate']; | ||||||
|  |     originalClinicID = json['OriginalClinicID']; | ||||||
|  |     personRate = json['PersonRate']; | ||||||
|  |     projectDistanceInKiloMeters = json['ProjectDistanceInKiloMeters']; | ||||||
|  |     projectNameBottom = json['ProjectNameBottom']; | ||||||
|  |     projectNameTop = json['ProjectNameTop']; | ||||||
|  |     qR = json['QR']; | ||||||
|  |     qRString = json['QRString']; | ||||||
|  |     rateNumber = json['RateNumber']; | ||||||
|  |     regionName = json['RegionName']; | ||||||
|  |     regionNameN = json['RegionNameN']; | ||||||
|  |     serviceID = json['ServiceID']; | ||||||
|  |     setupID = json['SetupID']; | ||||||
|  |     speciality = json['Speciality'] != null ? json['Speciality'].cast<String>() : []; | ||||||
|  |     // specialityN = json['SpecialityN'].cast<String>(); | ||||||
|  |     transactionType = json['TransactionType']; | ||||||
|  |     virtualEmploymentType = json['VirtualEmploymentType']; | ||||||
|  |     workingHours = json['WorkingHours']; | ||||||
|  |     vida3Id = json['vida3Id']; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() { | ||||||
|  |     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||||
|  |     data['ClinicID'] = this.clinicID; | ||||||
|  |     data['ClinicName'] = this.clinicName; | ||||||
|  |     data['ClinicNameN'] = this.clinicNameN; | ||||||
|  |     data['DoctorTitle'] = this.doctorTitle; | ||||||
|  |     data['ID'] = this.iD; | ||||||
|  |     data['Name'] = this.name; | ||||||
|  |     data['ProjectID'] = this.projectID; | ||||||
|  |     data['ProjectName'] = this.projectName; | ||||||
|  |     data['ActualDoctorRate'] = this.actualDoctorRate; | ||||||
|  |     data['ClinicRoomNo'] = this.clinicRoomNo; | ||||||
|  |     data['Date'] = this.date; | ||||||
|  |     data['DayName'] = this.dayName; | ||||||
|  |     data['DecimalDoctorRate'] = this.decimalDoctorRate; | ||||||
|  |     data['DoctorAvailability'] = this.doctorAvailability; | ||||||
|  |     data['DoctorID'] = this.doctorID; | ||||||
|  |     data['DoctorImageURL'] = this.doctorImageURL; | ||||||
|  |     data['DoctorMobileNumber'] = this.doctorMobileNumber; | ||||||
|  |     data['DoctorProfile'] = this.doctorProfile; | ||||||
|  |     data['DoctorProfileInfo'] = this.doctorProfileInfo; | ||||||
|  |     data['DoctorRate'] = this.doctorRate; | ||||||
|  |     data['DoctorStarsRate'] = this.doctorStarsRate; | ||||||
|  |     data['EmploymentType'] = this.employmentType; | ||||||
|  |     data['Gender'] = this.gender; | ||||||
|  |     data['GenderDescription'] = this.genderDescription; | ||||||
|  |     data['HISRegionId'] = this.hISRegionId; | ||||||
|  |     data['IsActive'] = this.isActive; | ||||||
|  |     data['IsAllowWaitList'] = this.isAllowWaitList; | ||||||
|  |     data['IsAppointmentAllowed'] = this.isAppointmentAllowed; | ||||||
|  |     data['IsDoctorAllowVedioCall'] = this.isDoctorAllowVedioCall; | ||||||
|  |     data['IsDoctorDummy'] = this.isDoctorDummy; | ||||||
|  |     data['IsDoctorHasPrePostImages'] = this.isDoctorHasPrePostImages; | ||||||
|  |     data['IsHMC'] = this.isHMC; | ||||||
|  |     data['IsHmg'] = this.isHmg; | ||||||
|  |     data['IsLiveCare'] = this.isLiveCare; | ||||||
|  |     data['Latitude'] = this.latitude; | ||||||
|  |     data['Longitude'] = this.longitude; | ||||||
|  |     data['NationalityFlagURL'] = this.nationalityFlagURL; | ||||||
|  |     data['NationalityID'] = this.nationalityID; | ||||||
|  |     data['NationalityName'] = this.nationalityName; | ||||||
|  |     data['NearestFreeSlot'] = this.nearestFreeSlot; | ||||||
|  |     data['NoOfFreeSlotsAvailable'] = this.noOfFreeSlotsAvailable; | ||||||
|  |     data['NoOfPatientsRate'] = this.noOfPatientsRate; | ||||||
|  |     data['OriginalClinicID'] = this.originalClinicID; | ||||||
|  |     data['PersonRate'] = this.personRate; | ||||||
|  |     data['ProjectDistanceInKiloMeters'] = this.projectDistanceInKiloMeters; | ||||||
|  |     data['ProjectNameBottom'] = this.projectNameBottom; | ||||||
|  |     data['ProjectNameTop'] = this.projectNameTop; | ||||||
|  |     data['QR'] = this.qR; | ||||||
|  |     data['QRString'] = this.qRString; | ||||||
|  |     data['RateNumber'] = this.rateNumber; | ||||||
|  |     data['RegionName'] = this.regionName; | ||||||
|  |     data['RegionNameN'] = this.regionNameN; | ||||||
|  |     data['ServiceID'] = this.serviceID; | ||||||
|  |     data['SetupID'] = this.setupID; | ||||||
|  |     data['Speciality'] = this.speciality; | ||||||
|  |     data['SpecialityN'] = this.specialityN; | ||||||
|  |     data['TransactionType'] = this.transactionType; | ||||||
|  |     data['VirtualEmploymentType'] = this.virtualEmploymentType; | ||||||
|  |     data['WorkingHours'] = this.workingHours; | ||||||
|  |     data['vida3Id'] = this.vida3Id; | ||||||
|  |     return data; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,36 @@ | |||||||
|  | class GetClinicsListResponseModel { | ||||||
|  |   int? clinicID; | ||||||
|  |   String? clinicDescription; | ||||||
|  |   String? clinicDescriptionN; | ||||||
|  |   int? age; | ||||||
|  |   int? gender; | ||||||
|  |   bool? isLiveCareClinicAndOnline; | ||||||
|  |   int? liveCareClinicID; | ||||||
|  |   int? liveCareServiceID; | ||||||
|  | 
 | ||||||
|  |   GetClinicsListResponseModel({this.clinicID, this.clinicDescription, this.clinicDescriptionN, this.age, this.gender, this.isLiveCareClinicAndOnline, this.liveCareClinicID, this.liveCareServiceID}); | ||||||
|  | 
 | ||||||
|  |   GetClinicsListResponseModel.fromJson(Map<String, dynamic> json) { | ||||||
|  |     clinicID = json['ClinicID']; | ||||||
|  |     clinicDescription = json['ClinicDescription']; | ||||||
|  |     clinicDescriptionN = json['ClinicDescriptionN']; | ||||||
|  |     age = json['Age']; | ||||||
|  |     gender = json['Gender']; | ||||||
|  |     isLiveCareClinicAndOnline = json['IsLiveCareClinicAndOnline']; | ||||||
|  |     liveCareClinicID = json['LiveCareClinicID']; | ||||||
|  |     liveCareServiceID = json['LiveCareServiceID']; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() { | ||||||
|  |     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||||
|  |     data['ClinicID'] = this.clinicID; | ||||||
|  |     data['ClinicDescription'] = this.clinicDescription; | ||||||
|  |     data['ClinicDescriptionN'] = this.clinicDescriptionN; | ||||||
|  |     data['Age'] = this.age; | ||||||
|  |     data['Gender'] = this.gender; | ||||||
|  |     data['IsLiveCareClinicAndOnline'] = this.isLiveCareClinicAndOnline; | ||||||
|  |     data['LiveCareClinicID'] = this.liveCareClinicID; | ||||||
|  |     data['LiveCareServiceID'] = this.liveCareServiceID; | ||||||
|  |     return data; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | class TimeSlot { | ||||||
|  |   String? isoTime; | ||||||
|  |   DateTime? start; | ||||||
|  |   DateTime? end; | ||||||
|  |   String? vidaDate; | ||||||
|  |   bool isSelected = false; | ||||||
|  | 
 | ||||||
|  |   TimeSlot({required this.isoTime, required this.start, required this.end, this.vidaDate}); | ||||||
|  | } | ||||||
| @ -0,0 +1,159 @@ | |||||||
|  | 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/presentation/lab/collapsing_list_view.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  | 
 | ||||||
|  | class DoctorProfilePage extends StatelessWidget { | ||||||
|  |   DoctorProfilePage({super.key}); | ||||||
|  | 
 | ||||||
|  |   late AppState appState; | ||||||
|  |   late BookAppointmentsViewModel bookAppointmentsViewModel; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false); | ||||||
|  |     appState = getIt.get<AppState>(); | ||||||
|  |     return Scaffold( | ||||||
|  |       backgroundColor: AppColors.bgScaffoldColor, | ||||||
|  |       body: Column( | ||||||
|  |         children: [ | ||||||
|  |           Expanded( | ||||||
|  |             child: CollapsingListView( | ||||||
|  |               title: "Doctor Profile".needTranslation, | ||||||
|  |               child: SingleChildScrollView( | ||||||
|  |                 child: Column( | ||||||
|  |                   crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                   children: [ | ||||||
|  |                     SizedBox(height: 24.h), | ||||||
|  |                     Row( | ||||||
|  |                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |                       children: [ | ||||||
|  |                         Row( | ||||||
|  |                           children: [ | ||||||
|  |                             Image.network( | ||||||
|  |                               bookAppointmentsViewModel.doctorsProfileResponseModel.doctorImageURL!, | ||||||
|  |                               width: 63.h, | ||||||
|  |                               height: 63.h, | ||||||
|  |                               fit: BoxFit.fill, | ||||||
|  |                             ).circle(100), | ||||||
|  |                             SizedBox(width: 8.h), | ||||||
|  |                             Column( | ||||||
|  |                               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                               children: [ | ||||||
|  |                                 ("${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorTitleForProfile} ${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName}") | ||||||
|  |                                     .toString() | ||||||
|  |                                     .toText28(isBold: true), | ||||||
|  |                                 (bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.isNotEmpty ? bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.first : "") | ||||||
|  |                                     .toString() | ||||||
|  |                                     .toText18(weight: FontWeight.w500, color: AppColors.primaryRedColor), | ||||||
|  |                               ], | ||||||
|  |                             ), | ||||||
|  |                           ], | ||||||
|  |                         ), | ||||||
|  |                         Image.network( | ||||||
|  |                           bookAppointmentsViewModel.doctorsProfileResponseModel.nationalityFlagURL!, | ||||||
|  |                           width: 32.h, | ||||||
|  |                           height: 32.h, | ||||||
|  |                           fit: BoxFit.fill, | ||||||
|  |                         ), | ||||||
|  |                       ], | ||||||
|  |                     ), | ||||||
|  |                     SizedBox(height: 12.h), | ||||||
|  |                     Wrap( | ||||||
|  |                       direction: Axis.horizontal, | ||||||
|  |                       spacing: 3.h, | ||||||
|  |                       runSpacing: 4.h, | ||||||
|  |                       children: [ | ||||||
|  |                         AppCustomChipWidget( | ||||||
|  |                           icon: AppAssets.rating_icon, | ||||||
|  |                           iconColor: AppColors.ratingColorYellow, | ||||||
|  |                           labelText: "Rating: ${bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate}".needTranslation, | ||||||
|  |                         ), | ||||||
|  |                         AppCustomChipWidget( | ||||||
|  |                           icon: AppAssets.rating_icon, | ||||||
|  |                           iconColor: AppColors.ratingColorYellow, | ||||||
|  |                           labelText: "Reviews: ${bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate}".needTranslation, | ||||||
|  |                         ), | ||||||
|  |                       ], | ||||||
|  |                     ), | ||||||
|  |                     SizedBox(height: 16.h), | ||||||
|  |                     Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), | ||||||
|  |                     SizedBox(height: 16.h), | ||||||
|  |                     "Biography".toText14(weight: FontWeight.w600, color: AppColors.textColor), | ||||||
|  |                     bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo!.toText12(fontWeight: FontWeight.w600, color: AppColors.greyTextColor), | ||||||
|  |                   ], | ||||||
|  |                 ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |           Container( | ||||||
|  |             decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |               color: AppColors.whiteColor, | ||||||
|  |               borderRadius: 24.h, | ||||||
|  |               hasShadow: true, | ||||||
|  |             ), | ||||||
|  |             child: CustomButton( | ||||||
|  |               text: "View available appointments".needTranslation, | ||||||
|  |               onPressed: () async { | ||||||
|  |                 LoaderBottomSheet.showLoader(); | ||||||
|  |                 await bookAppointmentsViewModel.getDoctorFreeSlots( | ||||||
|  |                     isBookingForLiveCare: false, | ||||||
|  |                     onSuccess: (dynamic respData) async { | ||||||
|  |                       LoaderBottomSheet.hideLoader(); | ||||||
|  |                       showCommonBottomSheetWithoutHeight( | ||||||
|  |                         title: "Pick a Date".needTranslation, | ||||||
|  |                         context, | ||||||
|  |                         child: AppointmentCalendar(), | ||||||
|  |                         isFullScreen: false, | ||||||
|  |                         isCloseButtonVisible: true, | ||||||
|  |                         callBackFunc: () {}, | ||||||
|  |                       ); | ||||||
|  |                     }, | ||||||
|  |                     onError: (err) { | ||||||
|  |                       LoaderBottomSheet.hideLoader(); | ||||||
|  |                       showCommonBottomSheetWithoutHeight( | ||||||
|  |                         context, | ||||||
|  |                         child: Utils.getErrorWidget(loadingText: err), | ||||||
|  |                         callBackFunc: () {}, | ||||||
|  |                         isFullScreen: false, | ||||||
|  |                         isCloseButtonVisible: true, | ||||||
|  |                       ); | ||||||
|  |                     }); | ||||||
|  |               }, | ||||||
|  |               backgroundColor: AppColors.primaryRedColor, | ||||||
|  |               borderColor: AppColors.primaryRedColor, | ||||||
|  |               textColor: AppColors.whiteColor, | ||||||
|  |               fontSize: 16, | ||||||
|  |               fontWeight: FontWeight.w500, | ||||||
|  |               borderRadius: 12, | ||||||
|  |               padding: EdgeInsets.fromLTRB(10, 0, 10, 0), | ||||||
|  |               height: 50.h, | ||||||
|  |               icon: AppAssets.calendar, | ||||||
|  |               iconColor: AppColors.whiteColor, | ||||||
|  |               iconSize: 20.h, | ||||||
|  |             ).paddingSymmetrical(24.h, 24.h), | ||||||
|  |           ), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,236 @@ | |||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/loading_utils.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/authentication/authentication_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  | 
 | ||||||
|  | class ReviewAppointmentPage extends StatefulWidget { | ||||||
|  |   const ReviewAppointmentPage({super.key}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   State<ReviewAppointmentPage> createState() => _ReviewAppointmentPageState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> { | ||||||
|  |   late AppState appState; | ||||||
|  |   late BookAppointmentsViewModel bookAppointmentsViewModel; | ||||||
|  |   late AuthenticationViewModel authVM; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false); | ||||||
|  |     authVM = Provider.of<AuthenticationViewModel>(context, listen: false); | ||||||
|  |     appState = getIt.get<AppState>(); | ||||||
|  |     return Scaffold( | ||||||
|  |       backgroundColor: AppColors.scaffoldBgColor, | ||||||
|  |       body: Column( | ||||||
|  |         children: [ | ||||||
|  |           Expanded( | ||||||
|  |             child: CollapsingListView( | ||||||
|  |               title: LocaleKeys.reviewAppointment.tr(context: context), | ||||||
|  |               child: SingleChildScrollView( | ||||||
|  |                 padding: EdgeInsets.symmetric(horizontal: 24.h), | ||||||
|  |                 child: Column( | ||||||
|  |                   crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                   children: [ | ||||||
|  |                     SizedBox(height: 24.h), | ||||||
|  |                     LocaleKeys.docInfo.tr(context: context).toText16(isBold: true), | ||||||
|  |                     SizedBox(height: 16.h), | ||||||
|  |                     Container( | ||||||
|  |                       decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |                         color: AppColors.whiteColor, | ||||||
|  |                         borderRadius: 24.h, | ||||||
|  |                         hasShadow: false, | ||||||
|  |                       ), | ||||||
|  |                       child: Padding( | ||||||
|  |                         padding: EdgeInsets.all(16.h), | ||||||
|  |                         child: Column( | ||||||
|  |                           crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                           children: [ | ||||||
|  |                             Row( | ||||||
|  |                               children: [ | ||||||
|  |                                 Image.network( | ||||||
|  |                                   bookAppointmentsViewModel.selectedDoctor.doctorImageURL!, | ||||||
|  |                                   width: 50.h, | ||||||
|  |                                   height: 50.h, | ||||||
|  |                                   fit: BoxFit.fill, | ||||||
|  |                                 ).circle(100), | ||||||
|  |                                 SizedBox(width: 8.h), | ||||||
|  |                                 Column( | ||||||
|  |                                   crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                                   children: [ | ||||||
|  |                                     Row( | ||||||
|  |                                       children: [ | ||||||
|  |                                         SizedBox( | ||||||
|  |                                           width: MediaQuery.of(context).size.width * 0.49, | ||||||
|  |                                           child: | ||||||
|  |                                               "${bookAppointmentsViewModel.selectedDoctor.doctorTitle} ${bookAppointmentsViewModel.selectedDoctor.name}".toString().toText16(isBold: true, maxlines: 1), | ||||||
|  |                                         ), | ||||||
|  |                                         Image.network( | ||||||
|  |                                           bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL!, | ||||||
|  |                                           width: 20.h, | ||||||
|  |                                           height: 15.h, | ||||||
|  |                                           fit: BoxFit.fill, | ||||||
|  |                                         ), | ||||||
|  |                                       ], | ||||||
|  |                                     ), | ||||||
|  |                                     SizedBox(height: 2.h), | ||||||
|  |                                     (bookAppointmentsViewModel.selectedDoctor.speciality!.isNotEmpty ? bookAppointmentsViewModel.selectedDoctor.speciality!.first : "") | ||||||
|  |                                         .toString() | ||||||
|  |                                         .toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 1), | ||||||
|  |                                   ], | ||||||
|  |                                 ), | ||||||
|  |                               ], | ||||||
|  |                             ), | ||||||
|  |                             SizedBox(height: 12.h), | ||||||
|  |                             Wrap( | ||||||
|  |                               direction: Axis.horizontal, | ||||||
|  |                               spacing: 8.h, | ||||||
|  |                               runSpacing: 8.h, | ||||||
|  |                               children: [ | ||||||
|  |                                 AppCustomChipWidget( | ||||||
|  |                                   labelText: "${LocaleKeys.clinic.tr(context: context)}: ${bookAppointmentsViewModel.selectedDoctor.clinicName}".needTranslation, | ||||||
|  |                                 ), | ||||||
|  |                                 AppCustomChipWidget( | ||||||
|  |                                   labelText: "${LocaleKeys.branch.tr(context: context)} ${bookAppointmentsViewModel.selectedDoctor.projectName}".needTranslation, | ||||||
|  |                                 ), | ||||||
|  |                                 AppCustomChipWidget( | ||||||
|  |                                   labelText: "${LocaleKeys.date.tr(context: context)}: ${bookAppointmentsViewModel.selectedAppointmentDate}".needTranslation, | ||||||
|  |                                 ), | ||||||
|  |                                 AppCustomChipWidget( | ||||||
|  |                                   labelText: "${LocaleKeys.time.tr(context: context)}: ${bookAppointmentsViewModel.selectedAppointmentTime}".needTranslation, | ||||||
|  |                                 ), | ||||||
|  |                               ], | ||||||
|  |                             ), | ||||||
|  |                           ], | ||||||
|  |                         ), | ||||||
|  |                       ), | ||||||
|  |                     ), | ||||||
|  |                     SizedBox(height: 24.h), | ||||||
|  |                     LocaleKeys.patientInfo.tr(context: context).toText16(isBold: true), | ||||||
|  |                     SizedBox(height: 16.h), | ||||||
|  |                     Container( | ||||||
|  |                       decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |                         color: AppColors.whiteColor, | ||||||
|  |                         borderRadius: 24.h, | ||||||
|  |                         hasShadow: false, | ||||||
|  |                       ), | ||||||
|  |                       child: Padding( | ||||||
|  |                         padding: EdgeInsets.all(16.h), | ||||||
|  |                         child: Row( | ||||||
|  |                           children: [ | ||||||
|  |                             Image.asset( | ||||||
|  |                               appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, | ||||||
|  |                               width: 52.h, | ||||||
|  |                               height: 52.h, | ||||||
|  |                             ), | ||||||
|  |                             SizedBox(width: 8.h), | ||||||
|  |                             Column( | ||||||
|  |                               children: [ | ||||||
|  |                                 "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText16(isBold: true), | ||||||
|  |                                 SizedBox(height: 8.h), | ||||||
|  |                                 AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} Years Old"), | ||||||
|  |                               ], | ||||||
|  |                             ), | ||||||
|  |                           ], | ||||||
|  |                         ), | ||||||
|  |                       ), | ||||||
|  |                     ), | ||||||
|  |                     SizedBox(height: 24.h), | ||||||
|  |                     "Hospital Information".needTranslation.toText16(isBold: true), | ||||||
|  |                     SizedBox(height: 16.h), | ||||||
|  |                     Container( | ||||||
|  |                       decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |                         color: AppColors.whiteColor, | ||||||
|  |                         borderRadius: 12.h, | ||||||
|  |                         hasShadow: false, | ||||||
|  |                       ), | ||||||
|  |                       child: Padding( | ||||||
|  |                         padding: EdgeInsets.all(16.h), | ||||||
|  |                         child: Row( | ||||||
|  |                           children: [ | ||||||
|  |                             bookAppointmentsViewModel.selectedDoctor.projectName!.toText16(isBold: true), | ||||||
|  |                           ], | ||||||
|  |                         ), | ||||||
|  |                       ), | ||||||
|  |                     ), | ||||||
|  |                   ], | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |           Container( | ||||||
|  |             decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |               color: AppColors.whiteColor, | ||||||
|  |               borderRadius: 24.h, | ||||||
|  |               hasShadow: true, | ||||||
|  |             ), | ||||||
|  |             child: CustomButton( | ||||||
|  |               text: LocaleKeys.bookAppo.tr(context: context), | ||||||
|  |               onPressed: () async { | ||||||
|  |                 initiateBookAppointment(); | ||||||
|  |               }, | ||||||
|  |               backgroundColor: AppColors.primaryRedColor, | ||||||
|  |               borderColor: AppColors.primaryRedColor, | ||||||
|  |               textColor: AppColors.whiteColor, | ||||||
|  |               fontSize: 16, | ||||||
|  |               fontWeight: FontWeight.w500, | ||||||
|  |               borderRadius: 12, | ||||||
|  |               padding: EdgeInsets.fromLTRB(10, 0, 10, 0), | ||||||
|  |               height: 50.h, | ||||||
|  |             ).paddingSymmetrical(24.h, 24.h), | ||||||
|  |           ), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void initiateBookAppointment() async { | ||||||
|  |     LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation); | ||||||
|  | 
 | ||||||
|  |     await bookAppointmentsViewModel.insertSpecificAppointment(onError: (err) { | ||||||
|  |       print(err.data["ErrorEndUserMessage"]); | ||||||
|  |       LoadingUtils.hideFullScreenLoader(); | ||||||
|  |     }, onSuccess: (apiResp) async { | ||||||
|  |       LoadingUtils.hideFullScreenLoader(); | ||||||
|  |       await Future.delayed(Duration(milliseconds: 50)).then((value) async { | ||||||
|  |         LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); | ||||||
|  |         await Future.delayed(Duration(milliseconds: 4000)).then((value) { | ||||||
|  |           LoadingUtils.hideFullScreenLoader(); | ||||||
|  |           Navigator.pushAndRemoveUntil( | ||||||
|  |               context, | ||||||
|  |               FadePage( | ||||||
|  |                 page: LandingNavigation(), | ||||||
|  |               ), | ||||||
|  |               (r) => false); | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // await Future.delayed(Duration(milliseconds: 4000)).then((value) async { | ||||||
|  |     //   LoadingUtils.hideFullScreenLoader(); | ||||||
|  | 
 | ||||||
|  |     // await Future.delayed(Duration(milliseconds: 50)).then((value) async { | ||||||
|  |     //   LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); | ||||||
|  |     //   await Future.delayed(Duration(milliseconds: 4000)).then((value) { | ||||||
|  |     //     LoadingUtils.hideFullScreenLoader(); | ||||||
|  |     //   }); | ||||||
|  |     // }); | ||||||
|  |     // }); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,212 @@ | |||||||
|  | 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'; | ||||||
|  | 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/doctor_profile_page.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/doctor_card.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/input_widget.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; | ||||||
|  | import 'package: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'; | ||||||
|  | 
 | ||||||
|  | class SearchDoctorByName extends StatefulWidget { | ||||||
|  |   const SearchDoctorByName({super.key}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   State<SearchDoctorByName> createState() => _SearchDoctorByNameState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _SearchDoctorByNameState extends State<SearchDoctorByName> { | ||||||
|  |   TextEditingController searchEditingController = TextEditingController(); | ||||||
|  | 
 | ||||||
|  |   FocusNode textFocusNode = FocusNode(); | ||||||
|  | 
 | ||||||
|  |   late AppState appState; | ||||||
|  |   late BookAppointmentsViewModel bookAppointmentsViewModel; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false); | ||||||
|  |     appState = getIt.get<AppState>(); | ||||||
|  |     return Scaffold( | ||||||
|  |       backgroundColor: AppColors.bgScaffoldColor, | ||||||
|  |       body: Column( | ||||||
|  |         children: [ | ||||||
|  |           Expanded( | ||||||
|  |             child: CollapsingListView( | ||||||
|  |               title: "Choose Doctor".needTranslation, | ||||||
|  |               child: SingleChildScrollView( | ||||||
|  |                 child: Padding( | ||||||
|  |                   padding: EdgeInsets.symmetric(horizontal: 24.h), | ||||||
|  |                   child: Column( | ||||||
|  |                     children: [ | ||||||
|  |                       SizedBox(height: 16.h), | ||||||
|  |                       TextInputWidget( | ||||||
|  |                         labelText: LocaleKeys.search.tr(context: context), | ||||||
|  |                         hintText: LocaleKeys.doctorName.tr(context: context), | ||||||
|  |                         controller: searchEditingController, | ||||||
|  |                         isEnable: true, | ||||||
|  |                         prefix: null, | ||||||
|  |                         autoFocus: false, | ||||||
|  |                         isBorderAllowed: false, | ||||||
|  |                         keyboardType: TextInputType.text, | ||||||
|  |                         focusNode: textFocusNode, | ||||||
|  |                         suffix: searchEditingController.text.isNotEmpty | ||||||
|  |                             ? GestureDetector( | ||||||
|  |                                 onTap: () { | ||||||
|  |                                   searchEditingController.clear(); | ||||||
|  |                                   // bookAppointmentsViewModel.filterClinics(""); | ||||||
|  |                                   textFocusNode.unfocus(); | ||||||
|  |                                 }, | ||||||
|  |                                 child: Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown), | ||||||
|  |                               ) | ||||||
|  |                             : null, | ||||||
|  |                         onChange: (value) { | ||||||
|  |                           // bookAppointmentsViewModel.filterClinics(value!); | ||||||
|  |                         }, | ||||||
|  |                         padding: EdgeInsets.symmetric( | ||||||
|  |                           vertical: ResponsiveExtension(10).h, | ||||||
|  |                           horizontal: ResponsiveExtension(15).h, | ||||||
|  |                         ), | ||||||
|  |                       ), | ||||||
|  |                       SizedBox(height: 16.h), | ||||||
|  |                       Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) { | ||||||
|  |                         return Column( | ||||||
|  |                           crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                           children: [ | ||||||
|  |                             bookAppointmentsVM.isDoctorSearchByNameStarted | ||||||
|  |                                 ? ListView.separated( | ||||||
|  |                                     padding: EdgeInsets.only(top: 24.h), | ||||||
|  |                                     shrinkWrap: true, | ||||||
|  |                                     physics: NeverScrollableScrollPhysics(), | ||||||
|  |                                     itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : bookAppointmentsVM.doctorsList.length, | ||||||
|  |                                     itemBuilder: (context, index) { | ||||||
|  |                                       return bookAppointmentsVM.isDoctorsListLoading | ||||||
|  |                                           ? DoctorCard( | ||||||
|  |                                               doctorsListResponseModel: DoctorsListResponseModel(), | ||||||
|  |                                               isLoading: true, | ||||||
|  |                                               bookAppointmentsViewModel: bookAppointmentsViewModel, | ||||||
|  |                                             ) | ||||||
|  |                                           : AnimationConfiguration.staggeredList( | ||||||
|  |                                               position: index, | ||||||
|  |                                               duration: const Duration(milliseconds: 500), | ||||||
|  |                                               child: SlideAnimation( | ||||||
|  |                                                 verticalOffset: 100.0, | ||||||
|  |                                                 child: FadeInAnimation( | ||||||
|  |                                                   child: AnimatedContainer( | ||||||
|  |                                                     duration: Duration(milliseconds: 300), | ||||||
|  |                                                     curve: Curves.easeInOut, | ||||||
|  |                                                     decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), | ||||||
|  |                                                     child: DoctorCard( | ||||||
|  |                                                       doctorsListResponseModel: bookAppointmentsVM.doctorsList[index], | ||||||
|  |                                                       isLoading: false, | ||||||
|  |                                                       bookAppointmentsViewModel: bookAppointmentsViewModel, | ||||||
|  |                                                     ).onPress(() async { | ||||||
|  |                                                       bookAppointmentsVM.setSelectedDoctor(bookAppointmentsVM.doctorsList[index]); | ||||||
|  |                                                       // bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel()); | ||||||
|  |                                                       LoaderBottomSheet.showLoader(); | ||||||
|  |                                                       await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) { | ||||||
|  |                                                         LoaderBottomSheet.hideLoader(); | ||||||
|  |                                                         Navigator.of(context).push( | ||||||
|  |                                                           FadePage( | ||||||
|  |                                                             page: DoctorProfilePage(), | ||||||
|  |                                                           ), | ||||||
|  |                                                         ); | ||||||
|  |                                                       }, onError: (err) { | ||||||
|  |                                                         LoaderBottomSheet.hideLoader(); | ||||||
|  |                                                         showCommonBottomSheetWithoutHeight( | ||||||
|  |                                                           context, | ||||||
|  |                                                           child: Utils.getErrorWidget(loadingText: err), | ||||||
|  |                                                           callBackFunc: () {}, | ||||||
|  |                                                           isFullScreen: false, | ||||||
|  |                                                           isCloseButtonVisible: true, | ||||||
|  |                                                         ); | ||||||
|  |                                                       }); | ||||||
|  |                                                     }), | ||||||
|  |                                                   ), | ||||||
|  |                                                 ), | ||||||
|  |                                               ), | ||||||
|  |                                             ); | ||||||
|  |                                     }, | ||||||
|  |                                     separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), | ||||||
|  |                                   ) | ||||||
|  |                                 : SizedBox.shrink(), | ||||||
|  |                             SizedBox(height: 24.h), | ||||||
|  |                           ], | ||||||
|  |                         ); | ||||||
|  |                       }), | ||||||
|  |                     ], | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |           Container( | ||||||
|  |             decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |               color: AppColors.whiteColor, | ||||||
|  |               borderRadius: 24.h, | ||||||
|  |               hasShadow: true, | ||||||
|  |             ), | ||||||
|  |             child: CustomButton( | ||||||
|  |               text: LocaleKeys.search.tr(context: context), | ||||||
|  |               onPressed: () async { | ||||||
|  |                 textFocusNode.unfocus(); | ||||||
|  |                 if (searchEditingController.text.isNotEmpty) { | ||||||
|  |                   bookAppointmentsViewModel.setIsDoctorSearchByNameStarted(true); | ||||||
|  |                   bookAppointmentsViewModel.setIsDoctorsListLoading(true); | ||||||
|  |                   // LoaderBottomSheet.showLoader(); | ||||||
|  |                   await bookAppointmentsViewModel.getDoctorsList( | ||||||
|  |                       doctorName: searchEditingController.text, | ||||||
|  |                       onSuccess: (dynamic respData) {}, | ||||||
|  |                       onError: (err) { | ||||||
|  |                         bookAppointmentsViewModel.setIsDoctorSearchByNameStarted(false); | ||||||
|  |                         showCommonBottomSheetWithoutHeight( | ||||||
|  |                           context, | ||||||
|  |                           child: Utils.getErrorWidget(loadingText: err), | ||||||
|  |                           callBackFunc: () {}, | ||||||
|  |                           isFullScreen: false, | ||||||
|  |                           isCloseButtonVisible: true, | ||||||
|  |                         ); | ||||||
|  |                       }); | ||||||
|  |                 } else { | ||||||
|  |                   showCommonBottomSheetWithoutHeight( | ||||||
|  |                     context, | ||||||
|  |                     child: Utils.getErrorWidget(loadingText: "Please enter doctor name to search"), | ||||||
|  |                     callBackFunc: () {}, | ||||||
|  |                     isFullScreen: false, | ||||||
|  |                     isCloseButtonVisible: true, | ||||||
|  |                   ); | ||||||
|  |                 } | ||||||
|  |               }, | ||||||
|  |               backgroundColor: AppColors.primaryRedColor, | ||||||
|  |               borderColor: AppColors.primaryRedColor, | ||||||
|  |               textColor: AppColors.whiteColor, | ||||||
|  |               fontSize: 16, | ||||||
|  |               fontWeight: FontWeight.w500, | ||||||
|  |               borderRadius: 12, | ||||||
|  |               padding: EdgeInsets.fromLTRB(10, 0, 10, 0), | ||||||
|  |               height: 50.h, | ||||||
|  |               icon: AppAssets.search_icon, | ||||||
|  |               iconColor: AppColors.whiteColor, | ||||||
|  |               iconSize: 20.h, | ||||||
|  |             ).paddingSymmetrical(24.h, 24.h), | ||||||
|  |           ), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,158 @@ | |||||||
|  | import 'dart:async'; | ||||||
|  | 
 | ||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/doctor_card.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/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/transitions/fade_page.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  | 
 | ||||||
|  | class SelectDoctorPage extends StatefulWidget { | ||||||
|  |   SelectDoctorPage({super.key}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   State<SelectDoctorPage> createState() => _SelectDoctorPageState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _SelectDoctorPageState extends State<SelectDoctorPage> { | ||||||
|  |   TextEditingController searchEditingController = TextEditingController(); | ||||||
|  | 
 | ||||||
|  |   FocusNode textFocusNode = FocusNode(); | ||||||
|  | 
 | ||||||
|  |   late AppState appState; | ||||||
|  |   late BookAppointmentsViewModel bookAppointmentsViewModel; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void initState() { | ||||||
|  |     scheduleMicrotask(() { | ||||||
|  |       bookAppointmentsViewModel.getDoctorsList(); | ||||||
|  |     }); | ||||||
|  |     super.initState(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false); | ||||||
|  |     appState = getIt.get<AppState>(); | ||||||
|  |     return Scaffold( | ||||||
|  |       backgroundColor: AppColors.bgScaffoldColor, | ||||||
|  |       body: CollapsingListView( | ||||||
|  |         title: "Choose Doctor".needTranslation, | ||||||
|  |         child: SingleChildScrollView( | ||||||
|  |           child: Padding( | ||||||
|  |             padding: EdgeInsets.symmetric(horizontal: 24.h), | ||||||
|  |             child: Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) { | ||||||
|  |               return Column( | ||||||
|  |                 crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                 children: [ | ||||||
|  |                   // TODO: Implement doctor filter functionality | ||||||
|  |                   SizedBox(height: 16.h), | ||||||
|  |                   TextInputWidget( | ||||||
|  |                     labelText: LocaleKeys.search.tr(context: context), | ||||||
|  |                     hintText: LocaleKeys.doctorName.tr(context: context), | ||||||
|  |                     controller: searchEditingController, | ||||||
|  |                     isEnable: true, | ||||||
|  |                     prefix: null, | ||||||
|  |                     autoFocus: false, | ||||||
|  |                     isBorderAllowed: false, | ||||||
|  |                     keyboardType: TextInputType.text, | ||||||
|  |                     focusNode: textFocusNode, | ||||||
|  |                     suffix: searchEditingController.text.isNotEmpty | ||||||
|  |                         ? GestureDetector( | ||||||
|  |                             onTap: () { | ||||||
|  |                               searchEditingController.clear(); | ||||||
|  |                               bookAppointmentsViewModel.filterClinics(""); | ||||||
|  |                               textFocusNode.unfocus(); | ||||||
|  |                             }, | ||||||
|  |                             child: Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown), | ||||||
|  |                           ) | ||||||
|  |                         : null, | ||||||
|  |                     onChange: (value) { | ||||||
|  |                       bookAppointmentsViewModel.filterClinics(value!); | ||||||
|  |                     }, | ||||||
|  |                     padding: EdgeInsets.symmetric( | ||||||
|  |                       vertical: ResponsiveExtension(10).h, | ||||||
|  |                       horizontal: ResponsiveExtension(15).h, | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |                   ListView.separated( | ||||||
|  |                     padding: EdgeInsets.only(top: 24.h), | ||||||
|  |                     shrinkWrap: true, | ||||||
|  |                     physics: NeverScrollableScrollPhysics(), | ||||||
|  |                     itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : bookAppointmentsVM.doctorsList.length, | ||||||
|  |                     itemBuilder: (context, index) { | ||||||
|  |                       return bookAppointmentsVM.isDoctorsListLoading | ||||||
|  |                           ? DoctorCard( | ||||||
|  |                               doctorsListResponseModel: DoctorsListResponseModel(), | ||||||
|  |                               isLoading: true, | ||||||
|  |                               bookAppointmentsViewModel: bookAppointmentsViewModel, | ||||||
|  |                             ) | ||||||
|  |                           : AnimationConfiguration.staggeredList( | ||||||
|  |                               position: index, | ||||||
|  |                               duration: const Duration(milliseconds: 500), | ||||||
|  |                               child: SlideAnimation( | ||||||
|  |                                 verticalOffset: 100.0, | ||||||
|  |                                 child: FadeInAnimation( | ||||||
|  |                                   child: AnimatedContainer( | ||||||
|  |                                     duration: Duration(milliseconds: 300), | ||||||
|  |                                     curve: Curves.easeInOut, | ||||||
|  |                                     decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), | ||||||
|  |                                     child: DoctorCard( | ||||||
|  |                                       doctorsListResponseModel: bookAppointmentsVM.doctorsList[index], | ||||||
|  |                                       isLoading: false, | ||||||
|  |                                       bookAppointmentsViewModel: bookAppointmentsViewModel, | ||||||
|  |                                     ).onPress(() async { | ||||||
|  |                                       bookAppointmentsVM.setSelectedDoctor(bookAppointmentsVM.doctorsList[index]); | ||||||
|  |                                       // bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel()); | ||||||
|  |                                       LoaderBottomSheet.showLoader(); | ||||||
|  |                                       await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) { | ||||||
|  |                                         LoaderBottomSheet.hideLoader(); | ||||||
|  |                                         Navigator.of(context).push( | ||||||
|  |                                           FadePage( | ||||||
|  |                                             page: DoctorProfilePage(), | ||||||
|  |                                           ), | ||||||
|  |                                         ); | ||||||
|  |                                       }, onError: (err) { | ||||||
|  |                                         LoaderBottomSheet.hideLoader(); | ||||||
|  |                                         showCommonBottomSheetWithoutHeight( | ||||||
|  |                                           context, | ||||||
|  |                                           child: Utils.getErrorWidget(loadingText: err), | ||||||
|  |                                           callBackFunc: () {}, | ||||||
|  |                                           isFullScreen: false, | ||||||
|  |                                           isCloseButtonVisible: true, | ||||||
|  |                                         ); | ||||||
|  |                                       }); | ||||||
|  |                                     }), | ||||||
|  |                                   ), | ||||||
|  |                                 ), | ||||||
|  |                               ), | ||||||
|  |                             ); | ||||||
|  |                     }, | ||||||
|  |                     separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), | ||||||
|  |                   ), | ||||||
|  |                   SizedBox(height: 24.h), | ||||||
|  |                 ], | ||||||
|  |               ); | ||||||
|  |             }), | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,146 @@ | |||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_page.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; | ||||||
|  | 
 | ||||||
|  | class SelectLivecareClinicPage extends StatelessWidget { | ||||||
|  |   const SelectLivecareClinicPage({super.key}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Scaffold( | ||||||
|  |       backgroundColor: AppColors.bgScaffoldColor, | ||||||
|  |       body: Column( | ||||||
|  |         children: [ | ||||||
|  |           Expanded( | ||||||
|  |             child: CollapsingListView( | ||||||
|  |               title: LocaleKeys.livecare.tr(context: context), | ||||||
|  |               child: SingleChildScrollView( | ||||||
|  |                 child: Column( | ||||||
|  |                   crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                   children: [ | ||||||
|  |                     SizedBox(height: 24.h), | ||||||
|  |                     LocaleKeys.livecareModalTop.tr(context: context).toText18(isBold: true), | ||||||
|  |                     SizedBox(height: 40.h), | ||||||
|  |                     Row( | ||||||
|  |                       children: [ | ||||||
|  |                         Utils.buildSvgWithAssets(icon: AppAssets.immediate_service_icon, width: 58.h, height: 58.h), | ||||||
|  |                         SizedBox(width: 18.h), | ||||||
|  |                         Expanded( | ||||||
|  |                           child: Column( | ||||||
|  |                             crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                             children: [ | ||||||
|  |                               "Immediate service".needTranslation.toText18(color: AppColors.textColor, isBold: true), | ||||||
|  |                               "No need to wait, you will get medical consultation immediately via video call".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), | ||||||
|  |                             ], | ||||||
|  |                           ), | ||||||
|  |                         ), | ||||||
|  |                       ], | ||||||
|  |                     ), | ||||||
|  |                     SizedBox(height: 24.h), | ||||||
|  |                     Row( | ||||||
|  |                       children: [ | ||||||
|  |                         Utils.buildSvgWithAssets(icon: AppAssets.no_visit_icon, width: 58.h, height: 58.h), | ||||||
|  |                         SizedBox(width: 18.h), | ||||||
|  |                         Expanded( | ||||||
|  |                           child: Column( | ||||||
|  |                             crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                             children: [ | ||||||
|  |                               "No visit required".needTranslation.toText18(color: AppColors.textColor, isBold: true), | ||||||
|  |                               LocaleKeys.livecarePoint5.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), | ||||||
|  |                             ], | ||||||
|  |                           ), | ||||||
|  |                         ), | ||||||
|  |                       ], | ||||||
|  |                     ), | ||||||
|  |                     SizedBox(height: 24.h), | ||||||
|  |                     Row( | ||||||
|  |                       children: [ | ||||||
|  |                         Utils.buildSvgWithAssets(icon: AppAssets.doctor_contact_icon, width: 58.h, height: 58.h), | ||||||
|  |                         SizedBox(width: 18.h), | ||||||
|  |                         Expanded( | ||||||
|  |                           child: Column( | ||||||
|  |                             crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                             children: [ | ||||||
|  |                               "Doctor will contact".needTranslation.toText18(color: AppColors.textColor, isBold: true), | ||||||
|  |                               "A specialised doctor will contact you and will be able to view your medical history".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), | ||||||
|  |                             ], | ||||||
|  |                           ), | ||||||
|  |                         ), | ||||||
|  |                       ], | ||||||
|  |                     ), | ||||||
|  |                     SizedBox(height: 24.h), | ||||||
|  |                     Row( | ||||||
|  |                       children: [ | ||||||
|  |                         Utils.buildSvgWithAssets(icon: AppAssets.free_med_delivery_icon, width: 58.h, height: 58.h), | ||||||
|  |                         SizedBox(width: 18.h), | ||||||
|  |                         Expanded( | ||||||
|  |                           child: Column( | ||||||
|  |                             crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                             children: [ | ||||||
|  |                               "Free medicine delivery".needTranslation.toText18(color: AppColors.textColor, isBold: true), | ||||||
|  |                               "Offers free medicine delivery for the LiveCare appointment".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), | ||||||
|  |                             ], | ||||||
|  |                           ), | ||||||
|  |                         ), | ||||||
|  |                       ], | ||||||
|  |                     ), | ||||||
|  |                   ], | ||||||
|  |                 ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |           Column( | ||||||
|  |             children: [ | ||||||
|  |               CustomButton( | ||||||
|  |                 text: "Yes please, I am in a hurry".needTranslation, | ||||||
|  |                 onPressed: () {}, | ||||||
|  |                 backgroundColor: AppColors.primaryRedColor, | ||||||
|  |                 borderColor: AppColors.primaryRedColor, | ||||||
|  |                 textColor: AppColors.whiteColor, | ||||||
|  |                 fontSize: 16, | ||||||
|  |                 fontWeight: FontWeight.w500, | ||||||
|  |                 borderRadius: 12, | ||||||
|  |                 padding: EdgeInsets.fromLTRB(10, 0, 10, 0), | ||||||
|  |                 height: 50.h, | ||||||
|  |                 icon: AppAssets.livecare_book_icon, | ||||||
|  |                 iconColor: AppColors.whiteColor, | ||||||
|  |                 iconSize: 18.h, | ||||||
|  |               ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |               SizedBox(height: 16.h), | ||||||
|  |               CustomButton( | ||||||
|  |                 text: "No, Thanks. I would like a physical visit".needTranslation, | ||||||
|  |                 onPressed: () { | ||||||
|  |                   Navigator.of(context).pop(); | ||||||
|  |                   Navigator.of(context).push( | ||||||
|  |                     FadePage( | ||||||
|  |                       page: SelectDoctorPage(), | ||||||
|  |                     ), | ||||||
|  |                   ); | ||||||
|  |                 }, | ||||||
|  |                 backgroundColor: AppColors.secondaryLightRedColor, | ||||||
|  |                 borderColor: AppColors.secondaryLightRedColor, | ||||||
|  |                 textColor: AppColors.primaryRedColor, | ||||||
|  |                 fontSize: 16, | ||||||
|  |                 fontWeight: FontWeight.w500, | ||||||
|  |                 borderRadius: 12, | ||||||
|  |                 padding: EdgeInsets.fromLTRB(10, 0, 10, 0), | ||||||
|  |                 height: 50.h, | ||||||
|  |               ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |               SizedBox(height: 24.h), | ||||||
|  |             ], | ||||||
|  |           ), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,390 @@ | |||||||
|  | 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'; | ||||||
|  | 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/extensions/string_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/models/timeslots.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/book_appointment/review_appointment_page.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/home/navigation_screen.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/transitions/fade_page.dart'; | ||||||
|  | import 'package:lottie/lottie.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  | import 'package:smooth_corner/smooth_corner.dart'; | ||||||
|  | import 'package:syncfusion_flutter_calendar/calendar.dart'; | ||||||
|  | 
 | ||||||
|  | class AppointmentCalendar extends StatefulWidget { | ||||||
|  |   const AppointmentCalendar({super.key}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   State<AppointmentCalendar> createState() => _AppointmentCalendarState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _AppointmentCalendarState extends State<AppointmentCalendar> { | ||||||
|  |   late CalendarController _calendarController; | ||||||
|  | 
 | ||||||
|  |   late AppState appState; | ||||||
|  |   late BookAppointmentsViewModel bookAppointmentsViewModel; | ||||||
|  |   late AuthenticationViewModel authVM; | ||||||
|  | 
 | ||||||
|  |   var selectedDate = ""; | ||||||
|  |   var selectedDateDisplay = ""; | ||||||
|  |   var selectedNextDate = ""; | ||||||
|  | 
 | ||||||
|  |   int selectedButtonIndex = 0; | ||||||
|  |   int selectedNextDayButtonIndex = -1; | ||||||
|  | 
 | ||||||
|  |   List<TimeSlot> dayEvents = []; | ||||||
|  |   List<TimeSlot> nextDayEvents = []; | ||||||
|  | 
 | ||||||
|  |   late Map<DateTime, List> _events; | ||||||
|  | 
 | ||||||
|  |   String selectedTime = ""; | ||||||
|  | 
 | ||||||
|  |   bool isWaitingAppointmentAvailable = false; | ||||||
|  |   final _selectedDay = DateTime.now(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void initState() { | ||||||
|  |     scheduleMicrotask(() { | ||||||
|  |       _calendarController = CalendarController(); | ||||||
|  |       _events = { | ||||||
|  |         _selectedDay: ['Event A0'] | ||||||
|  |       }; | ||||||
|  |       _onDaySelected(DateUtil.convertStringToDate(bookAppointmentsViewModel.freeSlotsResponse[0])); | ||||||
|  |     }); | ||||||
|  |     super.initState(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false); | ||||||
|  |     authVM = Provider.of<AuthenticationViewModel>(context, listen: false); | ||||||
|  |     appState = getIt.get<AppState>(); | ||||||
|  |     return Padding( | ||||||
|  |       padding: EdgeInsets.symmetric(horizontal: 0.h), | ||||||
|  |       child: Column( | ||||||
|  |         crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |         children: [ | ||||||
|  |           // SizedBox(height: 24.h), | ||||||
|  |           // Row( | ||||||
|  |           //   crossAxisAlignment: CrossAxisAlignment.center, | ||||||
|  |           //   children: [ | ||||||
|  |           //     "Pick a Date".toText20(weight: FontWeight.w600).expanded, | ||||||
|  |           //     Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, iconColor: Color(0xff2B353E)).onPress(() { | ||||||
|  |           //       Navigator.of(context).pop(); | ||||||
|  |           //     }), | ||||||
|  |           //   ], | ||||||
|  |           // ), | ||||||
|  |           SizedBox( | ||||||
|  |             height: 350.h, | ||||||
|  |             child: SfCalendar( | ||||||
|  |               controller: _calendarController, | ||||||
|  |               minDate: DateTime.now(), | ||||||
|  |               showNavigationArrow: true, | ||||||
|  |               headerHeight: 60.h, | ||||||
|  |               headerStyle: CalendarHeaderStyle( | ||||||
|  |                 backgroundColor: AppColors.scaffoldBgColor, | ||||||
|  |                 textAlign: TextAlign.start, | ||||||
|  |                 textStyle: TextStyle(fontSize: 18.fSize, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.primaryRedColor, fontFamily: "Poppins"), | ||||||
|  |               ), | ||||||
|  |               viewHeaderStyle: ViewHeaderStyle( | ||||||
|  |                 backgroundColor: AppColors.scaffoldBgColor, | ||||||
|  |                 dayTextStyle: TextStyle(fontSize: 14.fSize, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.textColor), | ||||||
|  |               ), | ||||||
|  |               view: CalendarView.month, | ||||||
|  |               todayHighlightColor: Colors.transparent, | ||||||
|  |               todayTextStyle: TextStyle(color: AppColors.textColor), | ||||||
|  |               selectionDecoration: ShapeDecoration( | ||||||
|  |                 color: AppColors.transparent, | ||||||
|  |                 shape: SmoothRectangleBorder( | ||||||
|  |                   borderRadius: BorderRadius.circular(12 ?? 0), | ||||||
|  |                   smoothness: 1, | ||||||
|  |                   side: BorderSide(color: AppColors.primaryRedColor, width: 1.5), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |               cellBorderColor: AppColors.transparent, | ||||||
|  |               dataSource: MeetingDataSource(_getDataSource()), | ||||||
|  |               monthCellBuilder: (context, details) => Padding( | ||||||
|  |                 padding: EdgeInsets.all(12.h), | ||||||
|  |                 child: details.date.day.toString().toText14( | ||||||
|  |                       isCenter: true, | ||||||
|  |                       color: details.date == _calendarController.selectedDate ? AppColors.primaryRedColor : AppColors.textColor, | ||||||
|  |                     ), | ||||||
|  |               ), | ||||||
|  |               monthViewSettings: MonthViewSettings( | ||||||
|  |                 dayFormat: "EEE", | ||||||
|  |                 appointmentDisplayMode: MonthAppointmentDisplayMode.indicator, | ||||||
|  |                 showTrailingAndLeadingDates: false, | ||||||
|  |                 appointmentDisplayCount: 1, | ||||||
|  |                 monthCellStyle: MonthCellStyle( | ||||||
|  |                   textStyle: TextStyle(fontSize: 19.fSize), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |               onTap: (CalendarTapDetails details) { | ||||||
|  |                 _calendarController.selectedDate = details.date; | ||||||
|  |                 _onDaySelected(details.date!); | ||||||
|  |               }, | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |           Transform.translate( | ||||||
|  |             offset: const Offset(0.0, -20.0), | ||||||
|  |             child: selectedDateDisplay.toText16(weight: FontWeight.w500), | ||||||
|  |           ), | ||||||
|  |           //TODO: Add Next Day Span here | ||||||
|  |           SizedBox( | ||||||
|  |             height: 100.h, | ||||||
|  |             child: SingleChildScrollView( | ||||||
|  |               scrollDirection: Axis.vertical, | ||||||
|  |               child: Wrap( | ||||||
|  |                 direction: Axis.horizontal, | ||||||
|  |                 alignment: WrapAlignment.start, | ||||||
|  |                 spacing: 8.h, | ||||||
|  |                 runSpacing: 8.h, | ||||||
|  |                 children: List.generate( | ||||||
|  |                   dayEvents.length, // Generate a large number of items to ensure scrolling | ||||||
|  |                   (index) => TimeSlotChip( | ||||||
|  |                     label: dayEvents[index].isoTime!, | ||||||
|  |                     isSelected: index == selectedButtonIndex, | ||||||
|  |                     onTap: () { | ||||||
|  |                       setState(() { | ||||||
|  |                         selectedButtonIndex = index; | ||||||
|  |                         selectedTime = dayEvents[index].isoTime!; | ||||||
|  |                       }); | ||||||
|  |                     }, | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |           SizedBox(height: 16.h), | ||||||
|  |           CustomButton( | ||||||
|  |             text: "Select".needTranslation, | ||||||
|  |             onPressed: () async { | ||||||
|  |               if (appState.isAuthenticated) { | ||||||
|  |                 bookAppointmentsViewModel.setSelectedAppointmentDateTime(selectedDate, selectedTime); | ||||||
|  |                 Navigator.of(context).pop(); | ||||||
|  |                 Navigator.of(context).push( | ||||||
|  |                   FadePage( | ||||||
|  |                     page: ReviewAppointmentPage(), | ||||||
|  |                   ), | ||||||
|  |                 ); | ||||||
|  |               } else { | ||||||
|  |                 showCommonBottomSheetWithoutHeight( | ||||||
|  |                   context, | ||||||
|  |                   title: LocaleKeys.notice.tr(context: context), | ||||||
|  |                   child: Column( | ||||||
|  |                     mainAxisAlignment: MainAxisAlignment.center, | ||||||
|  |                     crossAxisAlignment: CrossAxisAlignment.center, | ||||||
|  |                     children: [ | ||||||
|  |                       Lottie.asset(AppAnimations.errorAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), | ||||||
|  |                       SizedBox(height: 8.h), | ||||||
|  |                       (LocaleKeys.loginToUseService.tr()).toText16(color: AppColors.blackColor), | ||||||
|  |                       SizedBox(height: 16.h), | ||||||
|  |                       Row( | ||||||
|  |                         children: [ | ||||||
|  |                           Expanded( | ||||||
|  |                             child: CustomButton( | ||||||
|  |                               text: LocaleKeys.cancel.tr(), | ||||||
|  |                               onPressed: () { | ||||||
|  |                                 Navigator.of(context).pop(); | ||||||
|  |                               }, | ||||||
|  |                               backgroundColor: AppColors.secondaryLightRedColor, | ||||||
|  |                               borderColor: AppColors.secondaryLightRedColor, | ||||||
|  |                               textColor: AppColors.primaryRedColor, | ||||||
|  |                               icon: AppAssets.cancel, | ||||||
|  |                               iconColor: AppColors.primaryRedColor, | ||||||
|  |                             ), | ||||||
|  |                           ), | ||||||
|  |                           SizedBox(width: 8.h), | ||||||
|  |                           Expanded( | ||||||
|  |                             child: CustomButton( | ||||||
|  |                               text: LocaleKeys.confirm.tr(), | ||||||
|  |                               onPressed: () async { | ||||||
|  |                                 Navigator.of(context).pop(); | ||||||
|  |                                 Navigator.pushAndRemoveUntil( | ||||||
|  |                                     context, | ||||||
|  |                                     FadePage( | ||||||
|  |                                       page: LandingNavigation(), | ||||||
|  |                                     ), | ||||||
|  |                                     (r) => false); | ||||||
|  |                                 await authVM.onLoginPressed(); | ||||||
|  |                                 // Navigator.of(context).push( | ||||||
|  |                                 //   FadePage(page: MyAppointmentsPage()), | ||||||
|  |                                 // ); | ||||||
|  |                               }, | ||||||
|  |                               backgroundColor: AppColors.bgGreenColor, | ||||||
|  |                               borderColor: AppColors.bgGreenColor, | ||||||
|  |                               textColor: Colors.white, | ||||||
|  |                               icon: AppAssets.confirm, | ||||||
|  |                             ), | ||||||
|  |                           ), | ||||||
|  |                         ], | ||||||
|  |                       ), | ||||||
|  |                       SizedBox(height: 16.h), | ||||||
|  |                     ], | ||||||
|  |                   ).center, | ||||||
|  |                   callBackFunc: () {}, | ||||||
|  |                   isFullScreen: false, | ||||||
|  |                   isCloseButtonVisible: true, | ||||||
|  |                 ); | ||||||
|  |               } | ||||||
|  |             }, | ||||||
|  |             backgroundColor: AppColors.primaryRedColor, | ||||||
|  |             borderColor: AppColors.primaryRedColor, | ||||||
|  |             textColor: AppColors.whiteColor, | ||||||
|  |             fontSize: 16, | ||||||
|  |             fontWeight: FontWeight.w500, | ||||||
|  |             borderRadius: 12, | ||||||
|  |             padding: EdgeInsets.fromLTRB(10, 0, 10, 0), | ||||||
|  |             height: 50.h, | ||||||
|  |           ), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   List<Meeting> _getDataSource() { | ||||||
|  |     final List<Meeting> meetings = <Meeting>[]; | ||||||
|  |     for (var slot in bookAppointmentsViewModel.freeSlotsResponse) { | ||||||
|  |       final startTime = DateUtil.convertStringToDate(slot); | ||||||
|  | 
 | ||||||
|  |       final endTime = startTime.add(const Duration(minutes: 15)); | ||||||
|  | 
 | ||||||
|  |       meetings.add(Meeting( | ||||||
|  |           "Available", // Or leave empty with "" | ||||||
|  |           startTime, | ||||||
|  |           endTime, | ||||||
|  |           AppColors.primaryRedColor, | ||||||
|  |           false, | ||||||
|  |           "" // Optional notes | ||||||
|  |           )); | ||||||
|  |     } | ||||||
|  |     return meetings; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // TODO: | ||||||
|  |   openTimeSlotsPickerForDate(DateTime dateStart, List<TimeSlot> freeSlots) { | ||||||
|  |     dayEvents.clear(); | ||||||
|  |     DateTime dateStartObj = new DateTime(dateStart.year, dateStart.month, dateStart.day, 0, 0, 0, 0, 0); | ||||||
|  |     if (isWaitingAppointmentAvailable && DateUtils.isSameDay(dateStart, DateTime.now())) { | ||||||
|  |       dayEvents.add(TimeSlot(isoTime: "Waiting Appointment", start: DateTime.now(), end: DateTime.now(), vidaDate: "")); | ||||||
|  |     } | ||||||
|  |     freeSlots.forEach((v) { | ||||||
|  |       if (v.start == dateStartObj) dayEvents.add(v); | ||||||
|  |     }); | ||||||
|  |     selectedButtonIndex = 0; | ||||||
|  |     List<Map<String, dynamic>> timeList = []; | ||||||
|  |     for (var i = 0; i < dayEvents.length; i++) { | ||||||
|  |       Map<String, dynamic> timeSlot = {"isoTime": dayEvents[i].isoTime, "start": dayEvents[i].start.toString(), "end": dayEvents[i].end.toString(), "vidaDate": dayEvents[i].vidaDate}; | ||||||
|  |       timeList.add(timeSlot); | ||||||
|  |     } | ||||||
|  |     selectedTime = dayEvents[selectedButtonIndex].isoTime!; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void _onDaySelected(DateTime day) { | ||||||
|  |     final DateFormat formatter = DateFormat('yyyy-MM-dd'); | ||||||
|  |     setState(() { | ||||||
|  |       selectedDateDisplay = DateUtil.getMonthDayYearDateFormatted(day); | ||||||
|  |       selectedNextDate = DateUtil.getWeekDayMonthDayYearDateFormatted(day.add(Duration(days: 1)), "en"); | ||||||
|  |       _calendarController.selectedDate = day; | ||||||
|  |       openTimeSlotsPickerForDate(day, bookAppointmentsViewModel.docFreeSlots); | ||||||
|  |       selectedDate = formatter.format(day); | ||||||
|  |       selectedNextDayButtonIndex = -1; | ||||||
|  |       print(_calendarController.selectedDate); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class TimeSlotChip extends StatelessWidget { | ||||||
|  |   final String label; | ||||||
|  |   final bool isSelected; | ||||||
|  |   final VoidCallback? onTap; | ||||||
|  | 
 | ||||||
|  |   const TimeSlotChip({super.key, required this.label, this.isSelected = false, this.onTap}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return GestureDetector( | ||||||
|  |       onTap: onTap, | ||||||
|  |       child: Container( | ||||||
|  |         padding: EdgeInsets.symmetric(horizontal: 18.h, vertical: 8.h), | ||||||
|  |         decoration: ShapeDecoration( | ||||||
|  |           color: AppColors.whiteColor, | ||||||
|  |           shape: SmoothRectangleBorder( | ||||||
|  |             borderRadius: BorderRadius.circular(8.h), | ||||||
|  |             smoothness: 1, | ||||||
|  |             side: BorderSide(color: isSelected ? AppColors.primaryRedColor : AppColors.borderOnlyColor.withOpacity(0.2), width: 1), | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |         child: label.toText12( | ||||||
|  |           color: isSelected ? AppColors.primaryRedColor : Colors.black87, | ||||||
|  |           fontWeight: FontWeight.w500, | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class MeetingDataSource extends CalendarDataSource { | ||||||
|  |   MeetingDataSource(List<Meeting> source) { | ||||||
|  |     appointments = source; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   DateTime getStartTime(int index) { | ||||||
|  |     return _getMeetingData(index)!.from; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   DateTime getEndTime(int index) { | ||||||
|  |     return _getMeetingData(index)!.to; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String getSubject(int index) { | ||||||
|  |     return _getMeetingData(index)!.eventName; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Color getColor(int index) { | ||||||
|  |     return _getMeetingData(index)!.background; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   bool isAllDay(int index) { | ||||||
|  |     return _getMeetingData(index)!.isAllDay; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Meeting? _getMeetingData(int index) { | ||||||
|  |     final dynamic meeting = appointments?[index]; | ||||||
|  |     Meeting? meetingData; | ||||||
|  |     if (meeting is Meeting) { | ||||||
|  |       meetingData = meeting; | ||||||
|  |     } | ||||||
|  |     return meetingData; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class Meeting { | ||||||
|  |   Meeting(this.eventName, this.from, this.to, this.background, this.isAllDay, this.notes); | ||||||
|  | 
 | ||||||
|  |   String eventName; | ||||||
|  |   DateTime from; | ||||||
|  |   DateTime to; | ||||||
|  |   Color background; | ||||||
|  |   bool isAllDay; | ||||||
|  |   String notes; | ||||||
|  | } | ||||||
| @ -0,0 +1,42 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | 
 | ||||||
|  | class ClinicCard extends StatelessWidget { | ||||||
|  |   ClinicCard({super.key, required this.clinicsListResponseModel, required this.isLoading}); | ||||||
|  | 
 | ||||||
|  |   GetClinicsListResponseModel clinicsListResponseModel; | ||||||
|  |   bool isLoading; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Container( | ||||||
|  |       padding: EdgeInsets.all(16.h), | ||||||
|  |       decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |         color: AppColors.whiteColor, | ||||||
|  |         borderRadius: 24.h, | ||||||
|  |         hasShadow: false, | ||||||
|  |       ), | ||||||
|  |       child: Column( | ||||||
|  |         children: [ | ||||||
|  |           Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ | ||||||
|  |             "".toText16(isBold: true).toShimmer2(isShow: isLoading), | ||||||
|  |             (clinicsListResponseModel.isLiveCareClinicAndOnline ?? true) | ||||||
|  |                 ? Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 32.h, height: 32.h, fit: BoxFit.contain).toShimmer2(isShow: isLoading) | ||||||
|  |                 : SizedBox.shrink(), | ||||||
|  |           ]), | ||||||
|  |           SizedBox(height: 16.h), | ||||||
|  |           Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ | ||||||
|  |             Expanded(child: (isLoading ? "Cardiology" : clinicsListResponseModel.clinicDescription!).toText16(isBold: true).toShimmer2(isShow: isLoading)), | ||||||
|  |             Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor).toShimmer2(isShow: isLoading), | ||||||
|  |           ]), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,152 @@ | |||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_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/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; | ||||||
|  | 
 | ||||||
|  | class DoctorCard extends StatelessWidget { | ||||||
|  |   DoctorCard({super.key, required this.doctorsListResponseModel, required this.isLoading, required this.bookAppointmentsViewModel}); | ||||||
|  | 
 | ||||||
|  |   DoctorsListResponseModel doctorsListResponseModel; | ||||||
|  |   bool isLoading = false; | ||||||
|  |   BookAppointmentsViewModel bookAppointmentsViewModel; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Container( | ||||||
|  |       decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |         color: AppColors.whiteColor, | ||||||
|  |         borderRadius: 24.h, | ||||||
|  |         hasShadow: false, | ||||||
|  |       ), | ||||||
|  |       child: Padding( | ||||||
|  |         padding: EdgeInsets.all(14.h), | ||||||
|  |         child: Column( | ||||||
|  |           crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |           children: [ | ||||||
|  |             Row( | ||||||
|  |               children: [ | ||||||
|  |                 Image.network( | ||||||
|  |                   isLoading | ||||||
|  |                       ? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png" | ||||||
|  |                       : doctorsListResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png", | ||||||
|  |                   width: 63.h, | ||||||
|  |                   height: 63.h, | ||||||
|  |                   fit: BoxFit.fill, | ||||||
|  |                 ).circle(100).toShimmer2(isShow: isLoading), | ||||||
|  |                 SizedBox(width: 8.h), | ||||||
|  |                 Expanded( | ||||||
|  |                   flex: 9, | ||||||
|  |                   child: Column( | ||||||
|  |                     crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                     children: [ | ||||||
|  |                       Row( | ||||||
|  |                         children: [ | ||||||
|  |                           SizedBox( | ||||||
|  |                             width: MediaQuery.of(context).size.width * 0.49, | ||||||
|  |                             child: (isLoading ? "Dr John Smith" : "${doctorsListResponseModel.doctorTitle} ${doctorsListResponseModel.name}").toString().toText16(isBold: true, maxlines: 1), | ||||||
|  |                           ).toShimmer2(isShow: isLoading), | ||||||
|  |                           Image.network( | ||||||
|  |                             isLoading ? "https://hmgwebservices.com/Images/flag/SYR.png" : doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png", | ||||||
|  |                             width: 20.h, | ||||||
|  |                             height: 15.h, | ||||||
|  |                             fit: BoxFit.fill, | ||||||
|  |                           ).toShimmer2(isShow: isLoading), | ||||||
|  |                         ], | ||||||
|  |                       ), | ||||||
|  |                       SizedBox(height: 2.h), | ||||||
|  |                       (isLoading | ||||||
|  |                               ? "Consultant Cardiologist" | ||||||
|  |                               : doctorsListResponseModel.speciality!.isNotEmpty | ||||||
|  |                                   ? doctorsListResponseModel.speciality!.first | ||||||
|  |                                   : "") | ||||||
|  |                           .toString() | ||||||
|  |                           .toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 1) | ||||||
|  |                           .toShimmer2(isShow: isLoading), | ||||||
|  |                     ], | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |                 Expanded( | ||||||
|  |                   flex: 1, | ||||||
|  |                   child: Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown).toShimmer2(isShow: isLoading), | ||||||
|  |                 ), | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |             SizedBox(height: 12.h), | ||||||
|  |             Wrap( | ||||||
|  |               direction: Axis.horizontal, | ||||||
|  |               spacing: 3.h, | ||||||
|  |               runSpacing: 4.h, | ||||||
|  |               children: [ | ||||||
|  |                 AppCustomChipWidget( | ||||||
|  |                   labelText: "Clinic: ${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}".needTranslation, | ||||||
|  |                 ).toShimmer2(isShow: isLoading), | ||||||
|  |                 AppCustomChipWidget( | ||||||
|  |                   labelText: "Branch: ${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}".needTranslation, | ||||||
|  |                 ).toShimmer2(isShow: isLoading), | ||||||
|  |                 AppCustomChipWidget( | ||||||
|  |                   icon: AppAssets.rating_icon, | ||||||
|  |                   iconColor: AppColors.ratingColorYellow, | ||||||
|  |                   labelText: "Rating: ${isLoading ? 4.78 : doctorsListResponseModel.decimalDoctorRate}".needTranslation, | ||||||
|  |                 ).toShimmer2(isShow: isLoading), | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |             SizedBox(height: 12.h), | ||||||
|  |             CustomButton( | ||||||
|  |               text: LocaleKeys.bookAppo.tr(context: context), | ||||||
|  |               onPressed: () async { | ||||||
|  |                 bookAppointmentsViewModel.setSelectedDoctor(doctorsListResponseModel); | ||||||
|  |                 LoaderBottomSheet.showLoader(); | ||||||
|  |                 await bookAppointmentsViewModel.getDoctorFreeSlots( | ||||||
|  |                     isBookingForLiveCare: false, | ||||||
|  |                     onSuccess: (dynamic respData) async { | ||||||
|  |                       LoaderBottomSheet.hideLoader(); | ||||||
|  |                       showCommonBottomSheetWithoutHeight( | ||||||
|  |                         title: "Pick a Date".needTranslation, | ||||||
|  |                         context, | ||||||
|  |                         child: AppointmentCalendar(), | ||||||
|  |                         isFullScreen: false, | ||||||
|  |                         isCloseButtonVisible: true, | ||||||
|  |                         callBackFunc: () {}, | ||||||
|  |                       ); | ||||||
|  |                     }, | ||||||
|  |                     onError: (err) { | ||||||
|  |                       LoaderBottomSheet.hideLoader(); | ||||||
|  |                       showCommonBottomSheetWithoutHeight( | ||||||
|  |                         context, | ||||||
|  |                         child: Utils.getErrorWidget(loadingText: err), | ||||||
|  |                         callBackFunc: () {}, | ||||||
|  |                         isFullScreen: false, | ||||||
|  |                         isCloseButtonVisible: true, | ||||||
|  |                       ); | ||||||
|  |                     }); | ||||||
|  |               }, | ||||||
|  |               backgroundColor: Color(0xffFEE9EA), | ||||||
|  |               borderColor: Color(0xffFEE9EA), | ||||||
|  |               textColor: Color(0xffED1C2B), | ||||||
|  |               fontSize: 14, | ||||||
|  |               fontWeight: FontWeight.w500, | ||||||
|  |               borderRadius: 12, | ||||||
|  |               padding: EdgeInsets.fromLTRB(10, 0, 10, 0), | ||||||
|  |               height: 40.h, | ||||||
|  |               icon: AppAssets.add_icon, | ||||||
|  |               iconColor: AppColors.primaryRedColor, | ||||||
|  |               iconSize: 15.h, | ||||||
|  |             ).toShimmer2(isShow: isLoading), | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||