diff --git a/assets/animations/lottie/Nodata.json b/assets/animations/lottie/Nodata.json new file mode 100644 index 0000000..647c39d --- /dev/null +++ b/assets/animations/lottie/Nodata.json @@ -0,0 +1 @@ +{"nm":"Comp 1","ddd":0,"h":120,"w":120,"meta":{"g":"@lottiefiles/toolkit-js 0.33.2"},"layers":[{"ty":4,"nm":"ruoi","sr":1,"st":0,"op":50,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[60.531,10.945,0]},"s":{"a":0,"k":[100,100,100]},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[57.361,61.016,0],"t":0,"ti":[-13.9099960327148,5.27300262451172,0],"to":[-4.67500305175781,-4.12800598144531,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[57.699,41.796,0],"t":10.219,"ti":[-4.54498291015625,3.73400115966797,0],"to":[12.8159942626953,-4.85800170898438,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[79.084,33.982,0],"t":19.445,"ti":[20.0290069580078,1.20700073242188,0],"to":[6.61601257324219,-5.43799591064453,0]},{"s":[59.691,9.121,0],"t":35}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.033},"i":{"x":0.833,"y":0.967},"s":[100],"t":35},{"s":[0],"t":49}]}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"ruoi","ix":1,"cix":2,"np":3,"it":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[-0.994,0],[0,-0.994],[0.995,0],[0,0.994]],"o":[[0.995,0],[0,0.994],[-0.994,0],[0,-0.994]],"v":[[-0.001,-1.801],[1.801,-0.001],[-0.001,1.801],[-1.801,-0.001]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1804,0.1882,0.2235]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[62.4,13.144],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[-1.422,0],[0,-1.422],[1.421,0],[0,1.422]],"o":[[1.421,0],[0,1.422],[-1.422,0],[0,-1.422]],"v":[[0.001,-2.574],[2.574,0],[0.001,2.574],[-2.574,0]]}}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":10,"o":{"a":0,"k":100},"w":{"a":0,"k":0.7},"c":{"a":0,"k":[0.1804,0.1882,0.2235]}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[64.145,9.606],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 3","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[-1.996,0],[0,-1.996],[1.996,0],[0,1.996]],"o":[[1.996,0],[0,1.996],[-1.996,0],[0,-1.996]],"v":[[0,-3.614],[3.614,0],[0,3.614],[-3.614,0]]}}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":10,"o":{"a":0,"k":100},"w":{"a":0,"k":0.7},"c":{"a":0,"k":[0.1804,0.1882,0.2235]}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[57.957,10.552],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"tr","a":{"a":0,"k":[60.531,10.941],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[60.531,10.941],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1},{"ty":4,"nm":"Shape Layer 2","sr":1,"st":0,"op":50,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[-0.75,-0.75,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.033},"i":{"x":0.833,"y":0.967},"s":[100],"t":35},{"s":[0],"t":49}]}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 6","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[-13.91,5.273],[-4.545,3.734],[20.029,1.207]],"o":[[-4.675,-4.128],[12.816,-4.858],[6.616,-5.438],[0,0]],"v":[[-7.383,24.76],[-7.046,5.54],[14.34,-2.273],[-3.178,-24.76]]}}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100},"w":{"a":0,"k":1},"d":[{"nm":"dash","n":"d","v":{"a":0,"k":2.028}},{"nm":"gap","n":"g","v":{"a":0,"k":2.028}},{"nm":"offset","n":"o","v":{"a":0,"k":0}}],"c":{"a":0,"k":[0.1804,0.1882,0.2235]}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[67.87,37.631],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"tm","bm":0,"hd":false,"mn":"ADBE Vector Filter - Trim","nm":"Trim Paths 1","ix":2,"e":{"a":1,"k":[{"o":{"x":0.167,"y":0.033},"i":{"x":0.833,"y":0.953},"s":[0],"t":0},{"s":[100],"t":35}],"ix":2},"o":{"a":0,"k":0,"ix":3},"s":{"a":0,"k":0,"ix":1},"m":1}],"ind":2},{"ty":4,"nm":"im_emptyBox Outlines","sr":1,"st":0,"op":50,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[60,60,0]},"s":{"a":0,"k":[100,100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[60,60,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"box","ix":1,"cix":2,"np":4,"it":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 7","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-0.001,-16.607],[-32.143,-0.002],[-0.001,16.607],[32.144,-0.002]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8,0.8196,0.851]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[60,55.75],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 8","ix":2,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[12.856,-23.249],[0,-16.605],[-12.857,-23.249],[-45,-6.641],[-32.144,0.001],[-45,6.645],[-12.857,23.249],[0,16.609],[12.856,23.249],[45,6.645],[32.143,0.001],[45,-6.641]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.9373,0.9373,0.9373]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[60,55.748],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 9","ix":3,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-16.072,24.171],[16.072,11.312],[16.072,-24.171],[-16.072,-24.171]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.9529,0.9529,0.9529]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[76.072,83.33],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 10","ix":4,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-32.143,-24.171],[-32.143,11.311],[-0.001,24.171],[32.144,11.311],[32.144,-24.171]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8,0.8196,0.851]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[60,83.33],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"tr","a":{"a":0,"k":[60,60.186],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[60,60.186],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":3}],"v":"4.7.0","fr":25,"op":50,"ip":0,"assets":[]} \ No newline at end of file diff --git a/assets/images/svg/cardiology_clinic_icon.svg b/assets/images/svg/cardiology_clinic_icon.svg new file mode 100644 index 0000000..a4e73f1 --- /dev/null +++ b/assets/images/svg/cardiology_clinic_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/generic_clinic_icon.svg b/assets/images/svg/generic_clinic_icon.svg new file mode 100644 index 0000000..adb1303 --- /dev/null +++ b/assets/images/svg/generic_clinic_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/hmc.svg b/assets/images/svg/hmc.svg index a127cd9..824ca06 100644 --- a/assets/images/svg/hmc.svg +++ b/assets/images/svg/hmc.svg @@ -1,8 +1,5 @@ - - - + + + + diff --git a/assets/images/svg/hmg.svg b/assets/images/svg/hmg.svg index 7b199bf..ccaed0c 100644 --- a/assets/images/svg/hmg.svg +++ b/assets/images/svg/hmg.svg @@ -1,8 +1,5 @@ - - - + + + + diff --git a/assets/images/svg/small_livecare_icon.svg b/assets/images/svg/small_livecare_icon.svg new file mode 100644 index 0000000..a496914 --- /dev/null +++ b/assets/images/svg/small_livecare_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/walkin_appointment_icon.svg b/assets/images/svg/walkin_appointment_icon.svg new file mode 100644 index 0000000..ceafb38 --- /dev/null +++ b/assets/images/svg/walkin_appointment_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index a982556..3a65395 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -10,12 +10,12 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 40721B3F0DC8DB0598443DBF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E824BDA50EF77318022F59D /* Pods_Runner.framework */; }; 478CFA942E638C8E0064F3D7 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 478CFA932E638C8E0064F3D7 /* GoogleService-Info.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - B976FB9C47411C32B24D5E01 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ACE60DF9393168FD748550B3 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -44,16 +44,16 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 1E824BDA50EF77318022F59D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 478CFA932E638C8E0064F3D7 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 478CFA952E6E20A60064F3D7 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; + 62D069322AC3B532E0B4F137 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7595037DD52211B91157B0F3 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 8E12CEEB8E334EE22D5259D7 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -61,8 +61,8 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - ACE60DF9393168FD748550B3 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D6BB17A036DF7FCE75271203 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + B35471C63C8DD1B1DECDB3A5 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + D0FB40CE52522242F351743B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -70,7 +70,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B976FB9C47411C32B24D5E01 /* Pods_Runner.framework in Frameworks */, + 40721B3F0DC8DB0598443DBF /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -88,9 +88,9 @@ 79DD2093A1D9674C94359FC8 /* Pods */ = { isa = PBXGroup; children = ( - 8E12CEEB8E334EE22D5259D7 /* Pods-Runner.debug.xcconfig */, - 7595037DD52211B91157B0F3 /* Pods-Runner.release.xcconfig */, - D6BB17A036DF7FCE75271203 /* Pods-Runner.profile.xcconfig */, + 62D069322AC3B532E0B4F137 /* Pods-Runner.debug.xcconfig */, + D0FB40CE52522242F351743B /* Pods-Runner.release.xcconfig */, + B35471C63C8DD1B1DECDB3A5 /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -114,7 +114,7 @@ 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, 79DD2093A1D9674C94359FC8 /* Pods */, - A07D637C76A0ABB38659D189 /* Frameworks */, + C1312CE4ABEB9ED47FF21174 /* Frameworks */, ); sourceTree = ""; }; @@ -144,10 +144,10 @@ path = Runner; sourceTree = ""; }; - A07D637C76A0ABB38659D189 /* Frameworks */ = { + C1312CE4ABEB9ED47FF21174 /* Frameworks */ = { isa = PBXGroup; children = ( - ACE60DF9393168FD748550B3 /* Pods_Runner.framework */, + 1E824BDA50EF77318022F59D /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; @@ -176,15 +176,15 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - BFED6CCFE59BB148875A533B /* [CP] Check Pods Manifest.lock */, + E3E41B3FF6C30233949963A9 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 8372B02399CDF54531650AD4 /* [CP] Embed Pods Frameworks */, - 81DE7C26F41956799E954FCE /* [CP] Copy Pods Resources */, + 968C3FBB62CE1E0E307ECBCE /* [CP] Embed Pods Frameworks */, + CC3E3923FDE40035D0CA6762 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -273,24 +273,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 81DE7C26F41956799E954FCE /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 8372B02399CDF54531650AD4 /* [CP] Embed Pods Frameworks */ = { + 968C3FBB62CE1E0E307ECBCE /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -322,7 +305,24 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - BFED6CCFE59BB148875A533B /* [CP] Check Pods Manifest.lock */ = { + CC3E3923FDE40035D0CA6762 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + E3E41B3FF6C30233949963A9 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 62339bf..dc8b576 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -9,7 +9,6 @@ import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/routes/app_routes.dart'; import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart'; -import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:http/http.dart' as http; @@ -80,14 +79,11 @@ abstract class ApiClient { class ApiClientImp implements ApiClient { final _analytics = getIt(); - final LoggerService _loggerService; final AppState _appState; ApiClientImp({ - required LoggerService loggerService, required AppState appState, - }) : _appState = appState, - _loggerService = loggerService; + }) : _appState = appState; @override post( @@ -177,7 +173,7 @@ class ApiClientImp implements ApiClient { } // body['TokenID'] = "@dm!n"; - // body['PatientID'] = 3628599; + // body['PatientID'] = 4767477; } body.removeWhere((key, value) => value == null); @@ -200,7 +196,6 @@ class ApiClientImp implements ApiClient { final int statusCode = response.statusCode; log("response.body: ${response.body}"); if (statusCode < 200 || statusCode >= 400) { - var parsed = json.decode(utf8.decode(response.bodyBytes)); onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data")); logApiEndpointError(endPoint, 'Error While Fetching data', statusCode); } else { @@ -218,6 +213,7 @@ class ApiClientImp implements ApiClient { } else { if (parsed['ErrorType'] == 4) { //TODO : handle app update + onFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode, failureType: AppUpdateFailure("parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']")); logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode); } if (parsed['ErrorType'] == 2) { @@ -225,9 +221,9 @@ class ApiClientImp implements ApiClient { onFailure( parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode, - failureType: MessageStatusFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']), + failureType: UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url), ); - // logApiEndpointError(endPoint, "session logged out", statusCode); + // logApiEndpointError(endPoint, "session logged out", statusCode); } if (isAllowAny) { onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); @@ -248,6 +244,12 @@ class ApiClientImp implements ApiClient { } } else if (parsed['MessageStatus'] == 1 || parsed['SMSLoginRequired'] == true) { onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); + } else if (parsed['IsAuthenticated'] == false) { + onFailure( + "User is not Authenticated", + statusCode, + failureType: UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url), + ); } else if (parsed['MessageStatus'] == 2 && parsed['IsAuthenticated']) { if (parsed['SameClinicApptList'] != null) { onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); @@ -277,7 +279,6 @@ class ApiClientImp implements ApiClient { logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode); } } - } else if (!parsed['IsAuthenticated']) { } else { if (parsed['SameClinicApptList'] != null) { onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 206809d..9f5abce 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -727,7 +727,7 @@ const FAMILY_FILES= 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatu class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 424b90c..513a52a 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -134,6 +134,10 @@ class AppAssets { static const String minus = '$svgBasePath/minus.svg'; static const String home_lab_result_icon = '$svgBasePath/home_lab_result_icon.svg'; static const String visa_mastercard_icon = '$svgBasePath/visa_mastercard.svg'; + static const String small_livecare_icon = '$svgBasePath/small_livecare_icon.svg'; + static const String walkin_appointment_icon = '$svgBasePath/walkin_appointment_icon.svg'; + static const String cardiology_clinic_icon = '$svgBasePath/cardiology_clinic_icon.svg'; + static const String generic_clinic_icon = '$svgBasePath/generic_clinic_icon.svg'; static const String lab_result_indicator = '$svgBasePath/refernce_range_indicator.svg'; static const String ic_date_filter = '$svgBasePath/ic_date_filter.svg'; static const String ic_list = '$svgBasePath/ic_list.svg'; @@ -181,4 +185,5 @@ class AppAnimations { static const String errorAnimation = '$lottieBasePath/ErrorAnimation.json'; static const String warningAnimation = '$lottieBasePath/warningAnimation.json'; static const String splashLaunching = '$lottieBasePath/splash_launching.json'; + static const String noData = '$lottieBasePath/Nodata.json'; } diff --git a/lib/core/common_models/data_points.dart b/lib/core/common_models/data_points.dart index 89fa6e1..c35d190 100644 --- a/lib/core/common_models/data_points.dart +++ b/lib/core/common_models/data_points.dart @@ -6,7 +6,7 @@ class DataPoint { final double value; ///label shown on the bottom of the graph String label; - String refernceValue; + String referenceValue; String actualValue; DateTime time; String displayTime; @@ -14,7 +14,7 @@ class DataPoint { DataPoint( {required this.value, required this.label, - required this.refernceValue, + required this.referenceValue, required this.actualValue, required this.time, required this.displayTime, diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index b42706f..1fe2317 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -77,7 +77,7 @@ class AppDependencies { final sharedPreferences = await SharedPreferences.getInstance(); getIt.registerLazySingleton(() => CacheServiceImp(sharedPreferences: sharedPreferences, loggerService: getIt())); - getIt.registerLazySingleton(() => ApiClientImp(loggerService: getIt(), appState: getIt())); + getIt.registerLazySingleton(() => ApiClientImp(appState: getIt())); // Repositories getIt.registerLazySingleton(() => CommonRepoImp(loggerService: getIt())); @@ -126,11 +126,7 @@ class AppDependencies { ); getIt.registerLazySingleton( - () => MyAppointmentsViewModel( - myAppointmentsRepo: getIt(), - errorHandlerService: getIt(), - appState: getIt() - ), + () => MyAppointmentsViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()), ); getIt.registerLazySingleton( @@ -155,13 +151,7 @@ class AppDependencies { ); getIt.registerLazySingleton( - () => BookAppointmentsViewModel( - bookAppointmentsRepo: getIt(), - errorHandlerService: getIt(), - navigationService: getIt(), - myAppointmentsViewModel: getIt(), - locationUtils: getIt() - ), + () => BookAppointmentsViewModel(bookAppointmentsRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt(), myAppointmentsViewModel: getIt(), locationUtils: getIt()), ); getIt.registerLazySingleton( diff --git a/lib/core/exceptions/api_failure.dart b/lib/core/exceptions/api_failure.dart index c950195..0c5bc66 100644 --- a/lib/core/exceptions/api_failure.dart +++ b/lib/core/exceptions/api_failure.dart @@ -7,7 +7,9 @@ abstract class Failure extends Equatable implements Exception { } class ServerFailure extends Failure { - const ServerFailure(super.message); + final String url; + + const ServerFailure(super.message, {this.url = ""}); @override List get props => [message]; @@ -27,6 +29,13 @@ class MessageStatusFailure extends Failure { List get props => [message]; } +class AppUpdateFailure extends Failure { + const AppUpdateFailure(super.message); + + @override + List get props => [message]; +} + class StatusCodeFailure extends Failure { const StatusCodeFailure(super.message); @@ -34,6 +43,15 @@ class StatusCodeFailure extends Failure { List get props => [message]; } +class UnAuthenticatedUserFailure extends Failure { + final String url; + + const UnAuthenticatedUserFailure(super.message, {this.url = ""}); + + @override + List get props => [message]; +} + class ConnectivityFailure extends Failure { const ConnectivityFailure(super.message); @@ -56,7 +74,9 @@ class DataParsingFailure extends Failure { } class UnknownFailure extends Failure { - const UnknownFailure(super.message); + final String url; + + const UnknownFailure(super.message, {this.url = ""}); @override List get props => [message]; diff --git a/lib/core/utils/doctor_response_mapper.dart b/lib/core/utils/doctor_response_mapper.dart index bff4598..994e9a1 100644 --- a/lib/core/utils/doctor_response_mapper.dart +++ b/lib/core/utils/doctor_response_mapper.dart @@ -2,11 +2,12 @@ import 'dart:math'; import 'package:hmg_patient_app_new/core/cache_consts.dart' show CacheConst; import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils; +import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart' show RegionList, PatientDoctorAppointmentList, DoctorList, PatientDoctorAppointmentListByRegion; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel; class DoctorMapper{ - static Future getMappedDoctor(List doctorList, + static Future getMappedDoctor(List doctorList, {bool isArabic = false,double lat = 0.0,double long = 0.0}) async { RegionList regionList = RegionList(); @@ -41,16 +42,16 @@ class DoctorMapper{ isHMC: element.isHMC ); if(element.projectDistanceInKiloMeters!= null ){ - if(regionDoctorList!.distance>element.projectDistanceInKiloMeters){ - regionDoctorList.distance = element.projectDistanceInKiloMeters; + if(regionDoctorList!.distance>element.projectDistanceInKiloMeters!){ + regionDoctorList.distance = element.projectDistanceInKiloMeters!; } if (element.isHMC == true && - element.projectDistanceInKiloMeters < + element.projectDistanceInKiloMeters! < regionDoctorList.hmcDistance) { - regionDoctorList.hmcDistance = element.projectDistanceInKiloMeters; - } else if (element.projectDistanceInKiloMeters < + regionDoctorList.hmcDistance = element.projectDistanceInKiloMeters!; + } else if (element.projectDistanceInKiloMeters! < regionDoctorList.hmgDistance) { - regionDoctorList.hmgDistance = element.projectDistanceInKiloMeters; + regionDoctorList.hmgDistance = element.projectDistanceInKiloMeters!; } }else if (lat != 0&& @@ -65,12 +66,12 @@ class DoctorMapper{ regionDoctorList.distance = distance; } if (element.isHMC == true && - element.projectDistanceInKiloMeters < + (element.projectDistanceInKiloMeters??0) < regionDoctorList.hmcDistance) { - regionDoctorList.hmcDistance = element.projectDistanceInKiloMeters; - } else if (element.projectDistanceInKiloMeters < + regionDoctorList.hmcDistance = element.projectDistanceInKiloMeters??0; + } else if ((element.projectDistanceInKiloMeters??0) < regionDoctorList.hmgDistance) { - regionDoctorList.hmgDistance = element.projectDistanceInKiloMeters; + regionDoctorList.hmgDistance = element.projectDistanceInKiloMeters??0; } } targetList?.add(newAppointment); diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index 35b99bf..efa01d0 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -301,13 +301,17 @@ class Utils { return false; } - static Widget getNoDataWidget(BuildContext context, {String? errorText}) { + static Widget getNoDataWidget(BuildContext context, {double width = 124, double height = 124, String? noDataText, Widget callToActionButton = const SizedBox.shrink(), bool isSmallWidget = false}) { return Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - SvgPicture.asset('assets/images/svg/not_found.svg', width: 150.0, height: 150.0), - (errorText ?? LocaleKeys.noDataAvailable.tr()).toText16(isCenter: true).paddingOnly(top: 15), + SizedBox(height: isSmallWidget ? 0.h : 48.h), + Lottie.asset(AppAnimations.noData, repeat: false, reverse: false, frameRate: FrameRate(60), width: width.h, height: height.h, fit: BoxFit.fill), + SizedBox(height: 16.h), + (noDataText ?? LocaleKeys.noDataAvailable.tr()).toText16(weight: FontWeight.w500, color: AppColors.greyTextColor, isCenter: true).paddingSymmetrical(64.h, 0.h), + SizedBox(height: 16.h), + callToActionButton ], ).center; } diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index 0a11eda..b3f9617 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -1,16 +1,16 @@ import 'dart:convert'; import 'dart:developer'; import 'dart:io'; + import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/services.dart' show rootBundle; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show rootBundle; import 'package:get_it/get_it.dart'; import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/cache_consts.dart'; import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart'; import 'package:hmg_patient_app_new/core/common_models/privilege/HMCProjectListModel.dart'; -import 'package:hmg_patient_app_new/core/common_models/privilege/PrivilegeModel.dart'; import 'package:hmg_patient_app_new/core/common_models/privilege/ProjectDetailListModel.dart'; import 'package:hmg_patient_app_new/core/common_models/privilege/VidaPlusProjectListModel.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; @@ -19,7 +19,6 @@ import 'package:hmg_patient_app_new/core/utils/loading_utils.dart'; import 'package:hmg_patient_app_new/core/utils/request_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; -import 'package:hmg_patient_app_new/extensions/context_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart'; import 'package:hmg_patient_app_new/features/authentication/models/request_models/check_activation_code_register_request_model.dart'; @@ -40,7 +39,6 @@ import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/localauth_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; -import 'package:hmg_patient_app_new/widgets/bottomsheet/exception_bottom_sheet.dart'; import 'package:sms_otp_auto_verify/sms_otp_auto_verify.dart'; import 'models/request_models/get_user_mobile_device_data.dart'; diff --git a/lib/features/book_appointments/book_appointments_repo.dart b/lib/features/book_appointments/book_appointments_repo.dart index 81faf91..5e99585 100644 --- a/lib/features/book_appointments/book_appointments_repo.dart +++ b/lib/features/book_appointments/book_appointments_repo.dart @@ -7,6 +7,7 @@ import 'package:hmg_patient_app_new/core/utils/date_util.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/resp_models/get_livecare_clinics_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.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/services/logger_service.dart'; @@ -45,6 +46,26 @@ abstract class BookAppointmentsRepo { Future>>> getProjectList(); Future>>> getClinicsWithRespectToClinicId(String projectID); + + Future>>> getLiveCareScheduleClinics(int age, int genderID); + + Future>>> getLiveCareDoctorsList(int serviceID, int age, int genderID, {Function(dynamic)? onSuccess, Function(String)? onError}); + + Future>> getLiveCareDoctorFreeSlots(int clinicID, int serviceID, int projectID, int doctorId, bool isBookingForLiveCare, + {Function(dynamic)? onSuccess, Function(String)? onError}); + + Future>> insertSpecificAppointmentForLiveCare( + {required int docID, + required int clinicID, + required int projectID, + required String selectedTime, + required String selectedDate, + required int initialSlotDuration, + required int genderID, + required int userAge, + required int serviceID, + Function(dynamic)? onSuccess, + Function(String)? onError}); } class BookAppointmentsRepoImp implements BookAppointmentsRepo { @@ -443,4 +464,197 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>>> getLiveCareScheduleClinics(int age, int genderID) async { + Map mapDevice = {"Age": age, "Gender": genderID}; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_LIVECARE_SCHEDULE_CLINICS, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['ClinicsHaveScheduleList']; + // if (list == null || list.isEmpty) { + // throw Exception("lab list is empty"); + // } + + final clinicsList = list.map((item) => GetLiveCareClinicsResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: clinicsList, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future>>> getLiveCareDoctorsList(int serviceID, int age, int genderID, + {Function(dynamic)? onSuccess, Function(String)? onError}) async { + Map mapDevice = { + "ServiceID": serviceID, + "Age": age, + "Gender": genderID, + }; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_LIVECARE_SCHEDULE_CLINIC_DOCTOR_LIST, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + onError!(error); + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['DoctorByClinicIDList']; + + final doctorsList = list.map((item) => DoctorsListResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: doctorsList, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future> getLiveCareDoctorFreeSlots(int clinicID, int serviceID, int projectID, int doctorId, bool isBookingForLiveCare, + {Function(dynamic)? onSuccess, Function(String)? onError}) async { + Map mapDevice = { + "DoctorID": doctorId, + "IsBookingForLiveCare": true, + "ClinicID": clinicID, + "ServiceID": serviceID, + "ProjectID": projectID, + "OriginalClinicID": clinicID, + "days": 50, + "isReschadual": false, + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + GET_LIVECARE_SCHEDULE_DOCTOR_TIME_SLOTS, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future> insertSpecificAppointmentForLiveCare( + {required int docID, + required int clinicID, + required int projectID, + required String selectedTime, + required String selectedDate, + required int initialSlotDuration, + required int genderID, + required int userAge, + required int serviceID, + Function(dynamic)? onSuccess, + Function(String)? onError}) async { + Map mapDevice = { + "IsForLiveCare": true, + "ProjectID": projectID, + "ClinicID": clinicID, + "DoctorID": docID, + "ServiceID": serviceID, + "StartTime": selectedTime, + "SelectedTime": selectedTime, + "EndTime": selectedTime, + "InitialSlotDuration": initialSlotDuration, + "StrAppointmentDate": selectedDate, + "IsVirtual": false, + "BookedBy": 102, + "VisitType": 1, + "VisitFor": 1, + "GenderID": genderID, + "Age": userAge + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + INSERT_LIVECARE_SCHEDULE_APPOINTMENT, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final appointmentNo = response['AppointmentNo']; + + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: response["ErrorEndUserMessage"], + data: response, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index 6488dd8..5ef653a 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -29,6 +29,8 @@ import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:location/location.dart' show Location; +import 'models/resp_models/get_livecare_clinics_response_model.dart'; + class BookAppointmentsViewModel extends ChangeNotifier { int selectedTabIndex = 0; @@ -37,6 +39,8 @@ class BookAppointmentsViewModel extends ChangeNotifier { bool isDoctorProfileLoading = false; bool isDoctorSearchByNameStarted = false; + bool isLiveCareSchedule = false; + int initialSlotDuration = 0; LocationUtils locationUtils; @@ -44,12 +48,17 @@ class BookAppointmentsViewModel extends ChangeNotifier { List clinicsList = []; List _filteredClinicsList = []; + List liveCareClinicsList = []; + List get filteredClinicsList => _filteredClinicsList; List doctorsList = []; + List liveCareDoctorsList = []; + GetClinicsListResponseModel selectedClinic = GetClinicsListResponseModel(); DoctorsListResponseModel selectedDoctor = DoctorsListResponseModel(); + GetLiveCareClinicsResponseModel selectedLiveCareClinic = GetLiveCareClinicsResponseModel(); late DoctorsProfileResponseModel doctorsProfileResponseModel; @@ -78,7 +87,9 @@ class BookAppointmentsViewModel extends ChangeNotifier { bool shouldLoadSpecificClinic = false; String? currentlySelectedHospitalFromRegionFlow; - BookAppointmentsViewModel({required this.bookAppointmentsRepo, required this.errorHandlerService, required this.navigationService, required this.myAppointmentsViewModel, required this.locationUtils}) {; + BookAppointmentsViewModel( + {required this.bookAppointmentsRepo, required this.errorHandlerService, required this.navigationService, required this.myAppointmentsViewModel, required this.locationUtils}) { + ; initBookAppointmentViewModel(); } @@ -101,8 +112,11 @@ class BookAppointmentsViewModel extends ChangeNotifier { isClinicsListLoading = true; isDoctorsListLoading = true; isDoctorProfileLoading = true; + isLiveCareSchedule = false; + currentlySelectedHospitalFromRegionFlow = null; clinicsList.clear(); doctorsList.clear(); + liveCareClinicsList.clear(); // getLocation(); notifyListeners(); } @@ -154,6 +168,16 @@ class BookAppointmentsViewModel extends ChangeNotifier { notifyListeners(); } + setIsLiveCareSchedule(bool value) { + isLiveCareSchedule = value; + notifyListeners(); + } + + setLiveCareSelectedClinic(GetLiveCareClinicsResponseModel clinic) { + selectedLiveCareClinic = clinic; + notifyListeners(); + } + void onTabChanged(int index) { selectedTabIndex = index; notifyListeners(); @@ -161,10 +185,11 @@ class BookAppointmentsViewModel extends ChangeNotifier { /// this function will decide which clinic api to be called /// either api for region flow or the select clinic api - Future getClinics() async - { - if(shouldLoadSpecificClinic) { + Future getClinics() async { + if (shouldLoadSpecificClinic) { getRegionSelectedClinics(); + } else if (isLiveCareSchedule) { + getLiveCareScheduleClinics(); } else { getAllClinics(); } @@ -191,11 +216,58 @@ class BookAppointmentsViewModel extends ChangeNotifier { ); } + Future getLiveCareScheduleClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async { + liveCareClinicsList.clear(); + final result = await bookAppointmentsRepo.getLiveCareScheduleClinics(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + liveCareClinicsList = apiResponse.data!; + isClinicsListLoading = false; + initializeFilteredList(); + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + Future getLiveCareDoctorsList({Function(dynamic)? onSuccess, Function(String)? onError}) async { + doctorsList.clear(); + final result = + await bookAppointmentsRepo.getLiveCareDoctorsList(selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, onError: onError); + + 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) { + liveCareDoctorsList = apiResponse.data!; + isDoctorsListLoading = false; + // initializeFilteredList(); + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + //TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName Future getDoctorsList( {int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { doctorsList.clear(); - projectID = currentlySelectedHospitalFromRegionFlow != null?int.parse(currentlySelectedHospitalFromRegionFlow!):projectID; + projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID; final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName); result.fold( @@ -218,6 +290,42 @@ class BookAppointmentsViewModel extends ChangeNotifier { ); } + Future getMappedDoctors( + {int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { + filteredHospitalList = null; + hospitalList = null; + isRegionListLoading = true; + notifyListeners(); + projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID; + 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) async { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + var doctorList = apiResponse.data!; + hospitalList = await DoctorMapper.getMappedDoctor( + doctorList, + isArabic: _appState.isArabic(), + lat: _appState.userLat, + long: _appState.userLong, + ); + + var isLocationEnabled = (_appState.userLat != 0) && (_appState.userLong != 0); + hospitalList = await DoctorMapper.sortList(isLocationEnabled, hospitalList!); + + isRegionListLoading = false; + filteredHospitalList = hospitalList; + notifyListeners(); + } + }, + ); + } + Future getDoctorProfile({Function(dynamic)? onSuccess, Function(String)? onError}) async { final result = await bookAppointmentsRepo.getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError); @@ -238,7 +346,6 @@ class BookAppointmentsViewModel extends ChangeNotifier { ); } - //TODO: Handle the cases for LiveCare Schedule Future getDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { docFreeSlots.clear(); DateTime date; @@ -281,6 +388,50 @@ class BookAppointmentsViewModel extends ChangeNotifier { ); } + Future getLiveCareDoctorFreeSlots({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 _eventsParsed; + + final result = await bookAppointmentsRepo.getLiveCareDoctorFreeSlots( + selectedDoctor.clinicID ?? 0, selectedLiveCareClinic.serviceID ?? 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['PatientER_DoctorFreeSlots'] == null || apiResponse.data['PatientER_DoctorFreeSlots'].isEmpty) { + onError!("No free slots available".tr()); + return; + } + initialSlotDuration = apiResponse.data["InitialSlotDuration"]; + freeSlotsResponse = apiResponse.data['PatientER_DoctorFreeSlots']; + 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 cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel); @@ -300,18 +451,9 @@ class BookAppointmentsViewModel extends ChangeNotifier { ); } - //TODO: Handle the cases for LiveCare Schedule, Dental & Laser Clinics + //TODO: Handle the cases for Dental & Laser Clinics Future 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, + {String? procedureID, num? testTypeEnum, num? testProcedureEnum, int? invoiceNumber, @@ -413,28 +555,123 @@ class BookAppointmentsViewModel extends ChangeNotifier { ); } + Future insertSpecificAppointmentForLiveCare({Function(dynamic p1)? onSuccess, Function(dynamic p1)? onError}) async { + _appState = getIt(); + final result = await bookAppointmentsRepo.insertSpecificAppointmentForLiveCare( + docID: selectedDoctor.doctorID!, + clinicID: selectedDoctor.clinicID!, + projectID: selectedDoctor.projectID!, + serviceID: selectedLiveCareClinic.serviceID!, + 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!, + CustomPageRoute( + 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); + } + } + }, + ); + } + Future getRegionMappedProjectList() async { isRegionListLoading = true; notifyListeners(); final result = await bookAppointmentsRepo.getProjectList(); result.fold( - (failure) async => - await errorHandlerService.handleError(failure: failure), - (apiResponse) async { + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) async { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); } else if (apiResponse.messageStatus == 1) { var projectList = apiResponse.data!; - hospitalList = await DoctorMapper.getMappedHospitals(projectList, + hospitalList = await DoctorMapper.getMappedHospitals( + projectList, isArabic: _appState.isArabic(), lat: _appState.userLat, lng: _appState.userLong, ); var isLocationEnabled = (_appState.userLat != 0) && (_appState.userLong != 0); - hospitalList = - await DoctorMapper.sortList(isLocationEnabled, hospitalList!); + hospitalList = await DoctorMapper.sortList(isLocationEnabled, hospitalList!); isRegionListLoading = false; filteredHospitalList = hospitalList; @@ -450,22 +687,21 @@ class BookAppointmentsViewModel extends ChangeNotifier { } void filterHospitalListByString(String? value, String? selectedRegionId, bool isHMG) { - if(value ==null || value.isEmpty){ + if (value == null || value.isEmpty) { filteredHospitalList = hospitalList; } else { filteredHospitalList = RegionList(); - var list = isHMG - ? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList - : hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList; + var list = isHMG ? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList : hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList; - if(list != null && list.isEmpty){ notifyListeners(); return;} + if (list != null && list.isEmpty) { + notifyListeners(); + return; + } - var filteredList = list!.where((element) => - element.filterName!.toLowerCase().contains(value.toLowerCase()) - ).toList(); + var filteredList = list!.where((element) => element.filterName!.toLowerCase().contains(value.toLowerCase())).toList(); var regionData = PatientDoctorAppointmentListByRegion(); - if(isHMG){ + if (isHMG) { regionData.hmgDoctorList = filteredList; regionData.hmgSize = filteredList.length; } else { @@ -473,9 +709,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { regionData.hmcSize = filteredList.length; } - filteredHospitalList?.registeredDoctorMap = { - selectedRegionId! : regionData - }; + filteredHospitalList?.registeredDoctorMap = {selectedRegionId!: regionData}; } notifyListeners(); } @@ -498,12 +732,12 @@ class BookAppointmentsViewModel extends ChangeNotifier { currentlySelectedHospitalFromRegionFlow = mainProjectID; } - Future getRegionSelectedClinics() async{ - final result = await bookAppointmentsRepo.getClinicsWithRespectToClinicId(currentlySelectedHospitalFromRegionFlow??""); + Future getRegionSelectedClinics() async { + final result = await bookAppointmentsRepo.getClinicsWithRespectToClinicId(currentlySelectedHospitalFromRegionFlow ?? ""); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), - (apiResponse) { + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); } else if (apiResponse.messageStatus == 1) { @@ -515,12 +749,12 @@ class BookAppointmentsViewModel extends ChangeNotifier { }, ); } - void resetFilterList(){ + + void resetFilterList() { filteredHospitalList = hospitalList; } - - void getLocation(){ + void getLocation() { locationUtils.getLocation(); } } diff --git a/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart b/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart index 51a9403..5df6ed8 100644 --- a/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart +++ b/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart @@ -59,6 +59,13 @@ class DoctorsListResponseModel { int? virtualEmploymentType; dynamic workingHours; dynamic vida3Id; + String? region; + String? regionArabic; + String? regionEnglish; + String? regionID; + String? projectBottomName; + String? projectTopName; + DoctorsListResponseModel( {this.clinicID, @@ -120,7 +127,13 @@ class DoctorsListResponseModel { this.transactionType, this.virtualEmploymentType, this.workingHours, - this.vida3Id}); + this.vida3Id, + this.region, + this.regionArabic, + this.regionEnglish, + this.regionID, + this.projectBottomName, + this.projectTopName,}); DoctorsListResponseModel.fromJson(Map json) { clinicID = json['ClinicID']; @@ -183,6 +196,10 @@ class DoctorsListResponseModel { virtualEmploymentType = json['VirtualEmploymentType']; workingHours = json['WorkingHours']; vida3Id = json['vida3Id']; + regionArabic = json['RegionNameN']; + regionEnglish = json['RegionName']; + projectBottomName = json['ProjectNameBottom']; + projectTopName = json['ProjectNameTop']; } Map toJson() { @@ -249,4 +266,21 @@ class DoctorsListResponseModel { data['vida3Id'] = this.vida3Id; return data; } + + String? getRegionName(bool isArabic) { + if (isArabic) { + return regionArabic; + } + return regionEnglish; + } + String getProjectCompleteName(){ + return "${this.projectTopName} ${this.projectBottomName}"; + } + + String getProjectCompleteNameWithLocale({bool isArabic = false}) { + if (isArabic) { + return "${this.projectBottomName} ${this.projectTopName}"; + } + return "${this.projectTopName} ${this.projectBottomName}"; + } } diff --git a/lib/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart b/lib/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart index 0b0fa13..24d1c70 100644 --- a/lib/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart +++ b/lib/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart @@ -12,8 +12,8 @@ class GetClinicsListResponseModel { GetClinicsListResponseModel.fromJson(Map json) { clinicID = json['ClinicID']; - clinicDescription = json['ClinicDescription']; - clinicDescriptionN = json['ClinicDescriptionN']; + clinicDescription = json['ClinicDescription'] ?? "LiveCare Clinic"; + clinicDescriptionN = json['ClinicDescriptionN'] ?? "LiveCare Clinic"; age = json['Age']; gender = json['Gender']; isLiveCareClinicAndOnline = json['IsLiveCareClinicAndOnline']; diff --git a/lib/features/book_appointments/models/resp_models/get_livecare_clinics_response_model.dart b/lib/features/book_appointments/models/resp_models/get_livecare_clinics_response_model.dart new file mode 100644 index 0000000..2b66cfc --- /dev/null +++ b/lib/features/book_appointments/models/resp_models/get_livecare_clinics_response_model.dart @@ -0,0 +1,33 @@ +class GetLiveCareClinicsResponseModel { + int? clinicID; + int? serviceID; + int? projectID; + String? clinicDesc; + String? clinicDescN; + String? projectDesc; + String? projectDescN; + + GetLiveCareClinicsResponseModel({this.clinicID, this.serviceID, this.projectID, this.clinicDesc, this.clinicDescN, this.projectDesc, this.projectDescN}); + + GetLiveCareClinicsResponseModel.fromJson(Map json) { + clinicID = json['ClinicID']; + serviceID = json['ServiceID']; + projectID = json['ProjectID']; + clinicDesc = json['ClinicDesc'] ?? "LiveCare Clinic"; + clinicDescN = json['ClinicDescN']; + projectDesc = json['ProjectDesc']; + projectDescN = json['ProjectDescN']; + } + + Map toJson() { + final Map data = new Map(); + data['ClinicID'] = this.clinicID; + data['ServiceID'] = this.serviceID; + data['ProjectID'] = this.projectID; + data['ClinicDesc'] = this.clinicDesc; + data['ClinicDescN'] = this.clinicDescN; + data['ProjectDesc'] = this.projectDesc; + data['ProjectDescN'] = this.projectDescN; + return data; + } +} diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart index 811e5f8..5e89cd3 100644 --- a/lib/features/lab/lab_view_model.dart +++ b/lib/features/lab/lab_view_model.dart @@ -1,4 +1,5 @@ import 'dart:core'; +import 'dart:math'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -52,7 +53,8 @@ class LabViewModel extends ChangeNotifier { Set uniqueTests = {}; - double maxYForThreeDots = 0.0; + double maxY = 0.0; + double maxX = double.infinity; LabViewModel( {required this.labRepo, @@ -72,7 +74,12 @@ class LabViewModel extends ChangeNotifier { final result = await labRepo.getPatientLabOrders(); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async { + isLabOrdersLoading = false; + isLabResultsLoading = false; + notifyListeners(); + }, + // => await errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); @@ -136,6 +143,7 @@ class LabViewModel extends ChangeNotifier { LoaderBottomSheet.showLoader(); mainLabResults.clear(); filteredGraphValues.clear(); + maxY = double.negativeInfinity; final result = await labRepo.getPatientLabResults( laborder, @@ -160,16 +168,20 @@ class LabViewModel extends ChangeNotifier { try { var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!); - if (double.parse(element.resultValue!) > maxYForThreeDots) { - maxYForThreeDots = double.parse(element.resultValue!); + var resultValue = double.parse(element.resultValue!); + var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""); + if (resultValue>maxY) { + maxY = resultValue; + maxX = maxY; } + filteredGraphValues.add(DataPoint( - value: transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""), + value: transformedValue, actualValue:element.resultValue!, label: formatDateAsMMYY(dateTime), displayTime: resultDate(dateTime), time: DateUtil.convertStringToDate(element.verifiedOnDateTime), - refernceValue: element.calculatedResultFlag ?? "", + referenceValue: element.calculatedResultFlag ?? "", )); counter++; @@ -177,6 +189,7 @@ class LabViewModel extends ChangeNotifier { }); LabResult recentResult = recentThree.first; recentResult.verifiedOn = resultDate(DateUtil.convertStringToDate(recentResult.verifiedOnDateTime!)); + // filteredGraphValues = [filteredGraphValues.first]; navigationService.push(MaterialPageRoute( builder: (_) => LabResultDetails(recentLabResult: recentResult))); @@ -228,24 +241,32 @@ class LabViewModel extends ChangeNotifier { final normalizedValue = clampedValue / 100.0; // Normalize input to 0-1 // Map the normalized value to the target range bounds - final transformedValue = rangeStart + (normalizedValue * (rangeEnd - rangeStart)); - + final transformedValue = rangeStart + ((normalizedValue * (rangeEnd - rangeStart))); + debugPrint("the actual value is $inputValue"); + debugPrint("the flag is $flag"); + debugPrint("the transformed value is $transformedValue"); return transformedValue; } void getSelectedDateRange(DateTime? start, DateTime? end) { + maxY = double.negativeInfinity; + if(start == null && end == null) { - print("the dates are null"); mainLabResults.forEach((element) { final time = DateUtil.convertStringToDate(element.verifiedOnDateTime!); try{ + var resultValue = double.parse(element.resultValue!); + + var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""); + if (resultValue > maxY) { + maxY = resultValue; + } filteredGraphValues.add(DataPoint( - value: transformValueInRange(double.parse(element.resultValue!), - element.calculatedResultFlag ?? ""), + value: transformedValue, actualValue: element.resultValue!, label: formatDateAsMMYY(time), displayTime: resultDate(time), time: DateUtil.convertStringToDate(element.verifiedOnDateTime), - refernceValue: element.calculatedResultFlag ?? "", + referenceValue: element.calculatedResultFlag ?? "", )); }catch(e){ @@ -259,33 +280,34 @@ class LabViewModel extends ChangeNotifier { try { var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!); + var resultValue = double.parse(element.resultValue!); + var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""); + if (resultValue > maxY) { + maxY = resultValue; + } if (start != null && end == null) { if (dateTime.isAtSameMomentAs(start)) { filteredGraphValues.add(DataPoint( - value: transformValueInRange( - double.parse(element.resultValue!), - element.calculatedResultFlag ?? ""), + value: transformedValue, actualValue: element.resultValue!, label: formatDateAsMMYY(dateTime), displayTime: resultDate(dateTime), time: DateUtil.convertStringToDate(element.verifiedOnDateTime), - refernceValue: element.calculatedResultFlag ?? "")); + referenceValue: element.calculatedResultFlag ?? "")); } } else if (start != null && end != null) { if ((dateTime.isAfter(start)) && (dateTime.isBefore(end))) { filteredGraphValues.add(DataPoint( - value: transformValueInRange( - double.parse(element.resultValue!), - element.calculatedResultFlag ?? ""), + value: transformedValue, actualValue: element.resultValue!, label: formatDateAsMMYY(dateTime), displayTime: resultDate(dateTime), time: DateUtil.convertStringToDate(element.verifiedOnDateTime), - refernceValue: element.calculatedResultFlag ?? "")); + referenceValue: element.calculatedResultFlag ?? "")); } } } catch (e) {} @@ -299,8 +321,7 @@ class LabViewModel extends ChangeNotifier { String formatDateAsMMYY(DateTime date) { - String year = date.year.toString().substring(2); - return '${months[date.month-1]},$year'; + return '${months[date.month-1]}, ${date.year}'; } diff --git a/lib/features/my_appointments/appointment_via_region_viewmodel.dart b/lib/features/my_appointments/appointment_via_region_viewmodel.dart index 4829f17..852e678 100644 --- a/lib/features/my_appointments/appointment_via_region_viewmodel.dart +++ b/lib/features/my_appointments/appointment_via_region_viewmodel.dart @@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart' show ChangeNotifier; import 'package:hmg_patient_app_new/core/app_state.dart' show AppState; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_page.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; @@ -14,6 +15,11 @@ enum AppointmentViaRegionState { DOCTOR_SELECTION } +enum RegionBottomSheetType{ + FOR_REGION, + FOR_CLINIIC +} + class AppointmentViaRegionViewmodel extends ChangeNotifier { String? selectedRegionId; String? selectedFacilityType; @@ -23,6 +29,8 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier { AppointmentViaRegionState.REGION_SELECTION; final AppState appState; + RegionBottomSheetType regionBottomSheetType = RegionBottomSheetType.FOR_REGION; + AppointmentViaRegionViewmodel({required this.navigationService,required this.appState}); void setSelectedRegionId(String? regionId) { @@ -35,15 +43,20 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier { notifyListeners(); } + void setBottomSheetType(RegionBottomSheetType type) { + regionBottomSheetType = type; + notifyListeners(); + } + void setBottomSheetState(AppointmentViaRegionState state) { bottomSheetState = state; notifyListeners(); } - void handleLastStep(){ + void handleLastStepForRegion(){ navigationService.pop(); navigationService.push(CustomPageRoute( - page: SelectClinicPage(), + page: SelectClinicPage(isFromRegionFlow: true,), ),); } @@ -65,6 +78,7 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier { void flush() { setSelectedRegionId(null); setFacility(null); + setBottomSheetType(RegionBottomSheetType.FOR_REGION); setBottomSheetState(AppointmentViaRegionState.REGION_SELECTION); } @@ -73,4 +87,11 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier { } bool get isArabic => appState.isArabic(); + + void handleLastStepForClinic() { + navigationService.pop(); + navigationService.push(CustomPageRoute( + page: SelectDoctorPage(), + ),); + } } diff --git a/lib/features/my_appointments/models/resp_models/doctor_list_api_response.dart b/lib/features/my_appointments/models/resp_models/doctor_list_api_response.dart index c3155e1..c2b9add 100644 --- a/lib/features/my_appointments/models/resp_models/doctor_list_api_response.dart +++ b/lib/features/my_appointments/models/resp_models/doctor_list_api_response.dart @@ -1,4 +1,5 @@ +import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel; class DoctorList { @@ -236,7 +237,7 @@ class DoctorList { class PatientDoctorAppointmentList { String? filterName = ""; String? distanceInKMs = ""; - List? patientDoctorAppointmentList = []; + List? patientDoctorAppointmentList = []; String? projectTopName = ""; String? projectBottomName = ""; bool? isHMC; @@ -247,7 +248,7 @@ class PatientDoctorAppointmentList { this.distanceInKMs, this.projectTopName, this.projectBottomName, - DoctorList? patientDoctorAppointment, + DoctorsListResponseModel? patientDoctorAppointment, HospitalsModel? model, this.isHMC = false}) { if (model != null) { diff --git a/lib/features/my_appointments/my_appointments_repo.dart b/lib/features/my_appointments/my_appointments_repo.dart index e4745cb..99f7c7d 100644 --- a/lib/features/my_appointments/my_appointments_repo.dart +++ b/lib/features/my_appointments/my_appointments_repo.dart @@ -1,10 +1,13 @@ +import 'dart:io'; + import 'package:dartz/dartz.dart'; import 'package:hmg_patient_app_new/core/api/api_client.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; +import 'package:hmg_patient_app_new/core/cache_consts.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; -import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' - show HospitalsModel; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; @@ -12,7 +15,8 @@ import 'package:hmg_patient_app_new/services/logger_service.dart'; abstract class MyAppointmentsRepo { Future>>> getPatientAppointments({required bool isActiveAppointment, required bool isArrivedAppointments}); - Future>> getPatientShareAppointment({required int projectID, required int clinicID, required String appointmentNo}); + Future>> getPatientShareAppointment( + {required int projectID, required int clinicID, required String appointmentNo, required bool isLiveCareAppointment}); Future>> createAdvancePayment( {required String paymentMethodName, @@ -39,7 +43,7 @@ abstract class MyAppointmentsRepo { Future>>> getPatientDoctorsList(); - + Future>> insertLiveCareVIDARequest({required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}); } class MyAppointmentsRepoImp implements MyAppointmentsRepo { @@ -99,14 +103,15 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { } @override - Future>> getPatientShareAppointment({required int projectID, required int clinicID, required String appointmentNo}) async { - Map mapRequest = {"ProjectID": projectID, "ClinicID": clinicID, "AppointmentNo": appointmentNo, "IsActiveAppointment": true}; + Future>> getPatientShareAppointment( + {required int projectID, required int clinicID, required String appointmentNo, required bool isLiveCareAppointment}) async { + Map mapRequest = {"ProjectID": projectID, "ClinicID": clinicID, "AppointmentNo": appointmentNo, "IsActiveAppointment": true, "IsForLiveCare": isLiveCareAppointment}; try { GenericApiModel? apiResponse; Failure? failure; await apiClient.post( - GET_PATIENT_SHARE, + isLiveCareAppointment ? GET_PATIENT_SHARE_LIVECARE : GET_PATIENT_SHARE, body: mapRequest, onFailure: (error, statusCode, {messageStatus, failureType}) { failure = failureType; @@ -495,4 +500,50 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future> insertLiveCareVIDARequest({required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}) async { + Map requestBody = { + "AppointmentNo": patientAppointmentHistoryResponseModel.appointmentNo, + "AppointmentDate": patientAppointmentHistoryResponseModel.appointmentDate, + "ClientRequestID": clientRequestID, + "ClinicID": patientAppointmentHistoryResponseModel.clinicID, + "ProjectID": patientAppointmentHistoryResponseModel.projectID, + "ServiceID": patientAppointmentHistoryResponseModel.serviceID, + "AcceptedBy": patientAppointmentHistoryResponseModel.doctorID, + "IsFlutter": true, + "DeviceToken": await Utils.getStringFromPrefs(CacheConst.pushToken), + "VoipToken": "", // TODO: Add VoIP Token functionality + "IsVoip": Platform.isIOS ? true : false + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + INSERT_VIDA_REQUEST, + body: requestBody, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index 55fafc4..c553f0e 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -19,6 +19,8 @@ class MyAppointmentsViewModel extends ChangeNotifier { bool isTimeLineAppointmentsLoading = false; bool isPatientMyDoctorsLoading = false; + bool isAppointmentDataToBeLoaded = true; + List patientAppointmentsHistoryList = []; List patientUpcomingAppointmentsHistoryList = []; @@ -38,15 +40,17 @@ class MyAppointmentsViewModel extends ChangeNotifier { } initAppointmentsViewModel() { - patientAppointmentsHistoryList.clear(); - patientUpcomingAppointmentsHistoryList.clear(); - patientArrivedAppointmentsHistoryList.clear(); - patientTimelineAppointmentsList.clear(); - patientMyDoctorsList.clear(); - isMyAppointmentsLoading = true; + if (isAppointmentDataToBeLoaded) { + patientAppointmentsHistoryList.clear(); + patientUpcomingAppointmentsHistoryList.clear(); + patientArrivedAppointmentsHistoryList.clear(); + patientTimelineAppointmentsList.clear(); + isMyAppointmentsLoading = true; + isTimeLineAppointmentsLoading = true; + patientMyDoctorsList.clear(); + isPatientMyDoctorsLoading = true; + } isAppointmentPatientShareLoading = true; - isTimeLineAppointmentsLoading = true; - isPatientMyDoctorsLoading = true; notifyListeners(); } @@ -70,6 +74,11 @@ class MyAppointmentsViewModel extends ChangeNotifier { notifyListeners(); } + setIsAppointmentDataToBeLoaded(bool val) { + isAppointmentDataToBeLoaded = val; + notifyListeners(); + } + setAppointmentReminder(bool value, PatientAppointmentHistoryResponseModel item) { int index = patientAppointmentsHistoryList.indexOf(item); if (index != -1) { @@ -79,6 +88,7 @@ class MyAppointmentsViewModel extends ChangeNotifier { } Future getPatientAppointments(bool isActiveAppointment, bool isArrivedAppointments, {Function(dynamic)? onSuccess, Function(String)? onError}) async { + if (!isAppointmentDataToBeLoaded) return; patientAppointmentsHistoryList.clear(); patientUpcomingAppointmentsHistoryList.clear(); @@ -95,6 +105,7 @@ class MyAppointmentsViewModel extends ChangeNotifier { } else if (apiResponse.messageStatus == 1) { patientUpcomingAppointmentsHistoryList = apiResponse.data!; isMyAppointmentsLoading = false; + isAppointmentDataToBeLoaded = false; notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); @@ -127,8 +138,8 @@ class MyAppointmentsViewModel extends ChangeNotifier { print('All Appointments: ${patientAppointmentsHistoryList.length}'); } - Future getPatientShareAppointment(int projectID, int clinicID, String appointmentNo, {Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await myAppointmentsRepo.getPatientShareAppointment(projectID: projectID, clinicID: clinicID, appointmentNo: appointmentNo); + Future getPatientShareAppointment(int projectID, int clinicID, String appointmentNo, bool isLiveCareAppointment, {Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await myAppointmentsRepo.getPatientShareAppointment(projectID: projectID, clinicID: clinicID, appointmentNo: appointmentNo, isLiveCareAppointment: isLiveCareAppointment); result.fold( (failure) async => await errorHandlerService.handleError(failure: failure), @@ -284,6 +295,7 @@ class MyAppointmentsViewModel extends ChangeNotifier { } Future getPatientMyDoctors({Function(dynamic)? onSuccess, Function(String)? onError}) async { + if (!isAppointmentDataToBeLoaded) return; final result = await myAppointmentsRepo.getPatientDoctorsList(); result.fold( @@ -305,4 +317,24 @@ class MyAppointmentsViewModel extends ChangeNotifier { }, ); } + + Future insertLiveCareVIDARequest( + {required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await myAppointmentsRepo.insertLiveCareVIDARequest(clientRequestID: clientRequestID, 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); + } + } + }, + ); + } } diff --git a/lib/features/prescriptions/prescriptions_view_model.dart b/lib/features/prescriptions/prescriptions_view_model.dart index f5f32a7..ebc3be8 100644 --- a/lib/features/prescriptions/prescriptions_view_model.dart +++ b/lib/features/prescriptions/prescriptions_view_model.dart @@ -71,6 +71,7 @@ class PrescriptionsViewModel extends ChangeNotifier { // (failure) async => await errorHandlerService.handleError(failure: failure), (failure) async { isPrescriptionsOrdersLoading = false; + notifyListeners(); }, (apiResponse) { if (apiResponse.messageStatus == 2) { diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index 7a98b9d..6976188 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -22,7 +22,7 @@ import 'package:hmg_patient_app_new/presentation/appointments/appointment_paymen import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_doctor_card.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; @@ -81,33 +81,11 @@ class _AppointmentDetailsPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // "Appointment Details".needTranslation.toText20(isBold: true), - // if (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) - // CustomButton( - // text: "Report".needTranslation, - // onPressed: () {}, - // backgroundColor: AppColors.secondaryLightRedColor, - // borderColor: AppColors.secondaryLightRedColor, - // textColor: AppColors.primaryRedColor, - // fontSize: 14, - // fontWeight: FontWeight.w500, - // borderRadius: 12, - // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - // height: 40.h, - // iconSize: 16.h, - // icon: AppAssets.report_icon, - // iconColor: AppColors.primaryRedColor, - // ) - // ], - // ), - // SizedBox(height: 24.h), AppointmentDoctorCard( patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, onAskDoctorTap: () {}, onCancelTap: () async { + myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); showCommonBottomSheet(context, child: Utils.getLoadingWidget(), callBackFunc: (str) {}, @@ -162,42 +140,60 @@ class _AppointmentDetailsPageState extends State { ? "Not Confirmed".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500) : "Confirmed".needTranslation.toText12(color: AppColors.successColor, fontWeight: FontWeight.w500)), SizedBox(height: 16.h), - Stack( - children: [ - ClipRRect( - clipBehavior: Clip.hardEdge, - borderRadius: BorderRadius.circular(24), - child: Image.network( - "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng", - fit: BoxFit.contain, - ), - ), - Positioned( - bottom: 0, - child: SizedBox( - width: MediaQuery.of(context).size.width * 0.785, - child: CustomButton( - text: "Get Directions".needTranslation, - onPressed: () { - MapsLauncher.launchCoordinates(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!), - double.parse(widget.patientAppointmentHistoryResponseModel.longitude!), widget.patientAppointmentHistoryResponseModel.projectName); - }, - backgroundColor: AppColors.textColor.withOpacity(0.8), - borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01), - textColor: AppColors.whiteColor, - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12.h, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - icon: AppAssets.directions_icon, - iconColor: AppColors.whiteColor, - iconSize: 13.h, - ).paddingAll(12.h), + //TODO Add countdown timer in case of LiveCare Appointment + widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false + ? Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "The doctor will call you once the appointment time approaches." + .needTranslation + .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + ], + ), + ), + ], + ) + : Stack( + children: [ + ClipRRect( + clipBehavior: Clip.hardEdge, + borderRadius: BorderRadius.circular(24), + child: Image.network( + "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng", + fit: BoxFit.contain, + ), + ), + Positioned( + bottom: 0, + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.785, + child: CustomButton( + text: "Get Directions".needTranslation, + onPressed: () { + MapsLauncher.launchCoordinates(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!), + double.parse(widget.patientAppointmentHistoryResponseModel.longitude!), widget.patientAppointmentHistoryResponseModel.projectName); + }, + backgroundColor: AppColors.textColor.withOpacity(0.8), + borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01), + textColor: AppColors.whiteColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12.h, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + icon: AppAssets.directions_icon, + iconColor: AppColors.whiteColor, + iconSize: 13.h, + ).paddingAll(12.h), + ), + ), + ], ), - ), - ], - ), ], ), ), @@ -476,6 +472,7 @@ class _AppointmentDetailsPageState extends State { : CustomButton( text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), onPressed: () { + myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction); }, backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction), diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index 77d5d03..9d6b557 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -21,15 +21,14 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/services/cache_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; -import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; -import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; import 'package:smooth_corner/smooth_corner.dart'; @@ -61,6 +60,7 @@ class _AppointmentPaymentPageState extends State { widget.patientAppointmentHistoryResponseModel.projectID, widget.patientAppointmentHistoryResponseModel.clinicID, widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), + widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false, ); }); super.initState(); @@ -361,8 +361,7 @@ class _AppointmentPaymentPageState extends State { } void checkPaymentStatus() async { - showCommonBottomSheet(context, - child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false); + LoaderBottomSheet.showLoader(); await payfortViewModel.checkPaymentStatus( transactionID: transID, onSuccess: (apiResponse) async { @@ -388,6 +387,21 @@ class _AppointmentPaymentPageState extends State { onSuccess: (value) async { if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) { //TODO: Implement LiveCare Check-In API Call + await myAppointmentsViewModel.insertLiveCareVIDARequest( + clientRequestID: transID, + patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, + onSuccess: (apiResponse) { + Future.delayed(Duration(milliseconds: 500), () { + LoaderBottomSheet.hideLoader(); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }); + }, + onError: (error) {}); } else { await myAppointmentsViewModel.generateAppointmentQR( clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, @@ -396,16 +410,16 @@ class _AppointmentPaymentPageState extends State { isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!, onSuccess: (apiResponse) { Future.delayed(Duration(milliseconds: 500), () { - Navigator.of(context).pop(); + LoaderBottomSheet.hideLoader(); Navigator.pushAndRemoveUntil( context, CustomPageRoute( page: LandingNavigation(), ), (r) => false); - Navigator.of(context).push( - CustomPageRoute(page: MyAppointmentsPage()), - ); + // Navigator.of(context).push( + // CustomPageRoute(page: MyAppointmentsPage()), + // ); }); }); } diff --git a/lib/presentation/appointments/my_appointments_page.dart b/lib/presentation/appointments/my_appointments_page.dart index 5972435..2518c88 100644 --- a/lib/presentation/appointments/my_appointments_page.dart +++ b/lib/presentation/appointments/my_appointments_page.dart @@ -1,17 +1,23 @@ 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/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/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/custom_tab_bar.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:provider/provider.dart'; @@ -116,7 +122,30 @@ class _MyAppointmentsPageState extends State { ), ), ) - : Utils.getNoDataWidget(context); + : Utils.getNoDataWidget( + context, + noDataText: "You don't have any appointments yet.".needTranslation, + callToActionButton: CustomButton( + text: LocaleKeys.bookAppo.tr(context: context), + onPressed: () { + Navigator.of(context).push( + CustomPageRoute( + page: BookAppointmentPage(), + ), + ); + }, + 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, + icon: AppAssets.add_icon, + iconColor: AppColors.primaryRedColor, + ).paddingSymmetrical(48.h, 0.h), + ); }, separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), ), @@ -159,7 +188,30 @@ class _MyAppointmentsPageState extends State { ), ), ) - : Utils.getNoDataWidget(context); + : Utils.getNoDataWidget( + context, + noDataText: "You don't have any appointments yet.".needTranslation, + callToActionButton: CustomButton( + text: LocaleKeys.bookAppo.tr(context: context), + onPressed: () { + Navigator.of(context).push( + CustomPageRoute( + page: BookAppointmentPage(), + ), + ); + }, + 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, + icon: AppAssets.add_icon, + iconColor: AppColors.primaryRedColor, + ).paddingSymmetrical(48.h, 0.h), + ); }, separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), ), @@ -202,7 +254,30 @@ class _MyAppointmentsPageState extends State { ), ), ) - : Utils.getNoDataWidget(context); + : Utils.getNoDataWidget( + context, + noDataText: "You don't have any appointments yet.".needTranslation, + callToActionButton: CustomButton( + text: LocaleKeys.bookAppo.tr(context: context), + onPressed: () { + Navigator.of(context).push( + CustomPageRoute( + page: BookAppointmentPage(), + ), + ); + }, + 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, + icon: AppAssets.add_icon, + iconColor: AppColors.primaryRedColor, + ).paddingSymmetrical(48.h, 0.h), + ); }, separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), ), @@ -213,3 +288,4 @@ class _MyAppointmentsPageState extends State { } } } + diff --git a/lib/presentation/appointments/my_doctors_page.dart b/lib/presentation/appointments/my_doctors_page.dart index 0b002c9..2fedfd8 100644 --- a/lib/presentation/appointments/my_doctors_page.dart +++ b/lib/presentation/appointments/my_doctors_page.dart @@ -10,7 +10,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:provider/provider.dart'; diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index c345099..152436a 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -18,6 +18,7 @@ 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/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; +import 'package:smooth_corner/smooth_corner.dart'; class AppointmentCard extends StatefulWidget { AppointmentCard({super.key, required this.patientAppointmentHistoryResponseModel, required this.myAppointmentsViewModel, this.isLoading = false, this.isFromHomePage = false}); @@ -63,44 +64,38 @@ class _AppointmentCardState extends State { spacing: 6.h, runSpacing: 6.h, children: [ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - CustomButton( - text: widget.isLoading - ? "OutPatient" - : appState.isArabic() - ? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! - : widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!, - onPressed: () {}, - backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), - borderColor: AppColors.primaryRedColor.withOpacity(0.0), - textColor: AppColors.primaryRedColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 30.h, - ), - ], - ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - CustomButton( - text: widget.isLoading ? "Booked" : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!), - onPressed: () {}, - backgroundColor: AppColors.successColor.withOpacity(0.1), - borderColor: AppColors.successColor.withOpacity(0.0), - textColor: AppColors.successColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 30.h, - ), - ], - ), + AppCustomChipWidget( + icon: widget.isLoading + ? AppAssets.walkin_appointment_icon + : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon), + iconColor: widget.isLoading + ? AppColors.textColor + : !widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! + ? AppColors.textColor + : AppColors.whiteColor, + labelText: widget.isLoading + ? "Walk In" + : widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! + ? LocaleKeys.livecare.tr(context: context) + : "Walk In".needTranslation, + backgroundColor: + widget.isLoading ? AppColors.greyColor : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor), + textColor: widget.isLoading ? AppColors.textColor : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor), + ).toShimmer2(isShow: widget.isLoading), + AppCustomChipWidget( + labelText: widget.isLoading + ? "OutPatient" + : appState.isArabic() + ? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! + : widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!, + backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), + textColor: AppColors.primaryRedColor, + ).toShimmer2(isShow: widget.isLoading), + AppCustomChipWidget( + labelText: widget.isLoading ? "Booked" : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!), + backgroundColor: AppColors.successColor.withOpacity(0.1), + textColor: AppColors.successColor, + ).toShimmer2(isShow: widget.isLoading), ], ).toShimmer2(isShow: widget.isLoading), ), diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart index da00d88..b35305f 100644 --- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart +++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart @@ -59,6 +59,13 @@ class AppointmentDoctorCard extends StatelessWidget { icon: AppAssets.doctor_calendar_icon, labelText: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}, ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}"), + AppCustomChipWidget( + icon: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon, + iconColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor, + labelText: patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? LocaleKeys.livecare.tr(context: context) : "Walk In".needTranslation, + backgroundColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor, + textColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor, + ), AppCustomChipWidget(icon: AppAssets.rating_icon, iconColor: AppColors.ratingColorYellow, labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"), ], ), @@ -113,29 +120,8 @@ class AppointmentDoctorCard extends StatelessWidget { iconSize: 16.h, ); } else { - return Row( - children: [ - Expanded( - child: CustomButton( - text: LocaleKeys.reschedule.tr(), - onPressed: () { - onRescheduleTap(); - }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12.h, - height: 40.h, - icon: AppAssets.appointment_calendar_icon, - iconColor: AppColors.primaryRedColor, - iconSize: 16.h, - ), - ), - SizedBox(width: 16.h), - Expanded( - child: CustomButton( + return patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false + ? CustomButton( text: LocaleKeys.cancel.tr(), onPressed: () { onCancelTap(); @@ -150,10 +136,48 @@ class AppointmentDoctorCard extends StatelessWidget { icon: AppAssets.cancel, iconColor: AppColors.whiteColor, iconSize: 16.h, - ), - ), - ], - ); + ) + : Row( + children: [ + Expanded( + child: CustomButton( + text: LocaleKeys.reschedule.tr(), + onPressed: () { + onRescheduleTap(); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12.h, + height: 40.h, + icon: AppAssets.appointment_calendar_icon, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + ), + ), + SizedBox(width: 16.h), + Expanded( + child: CustomButton( + text: LocaleKeys.cancel.tr(), + onPressed: () { + onCancelTap(); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12.h, + height: 40.h, + icon: AppAssets.cancel, + iconColor: AppColors.whiteColor, + iconSize: 16.h, + ), + ), + ], + ); } } } diff --git a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart index 43ce9c6..44cc43e 100644 --- a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart +++ b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart @@ -12,7 +12,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_vie import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:provider/provider.dart'; @@ -96,9 +96,16 @@ class HospitalBottomSheetBody extends StatelessWidget { isLocationEnabled: appointmentsViewModel.isLocationEnabled(), ).onPress(() { regionalViewModel.setHospitalModel(hospital); - regionalViewModel.setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION); - regionalViewModel.handleLastStep(); - });}, + if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_REGION) { + regionalViewModel.setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION); + regionalViewModel.handleLastStepForRegion(); + }else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_CLINIIC) { + + regionalViewModel.setBottomSheetState(AppointmentViaRegionState.DOCTOR_SELECTION); + regionalViewModel.handleLastStepForClinic(); + + } + });}, separatorBuilder: (_, __) => SizedBox( height: 16.h, ), diff --git a/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart index 180185f..09c7c40 100644 --- a/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart +++ b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart @@ -6,14 +6,11 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; -import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart' - show MyAppointmentsViewModel; -import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart' - show RegionListItem; +import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart' show MyAppointmentsViewModel; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart' show RegionListItem; import 'package:provider/provider.dart'; class RegionBottomSheetBody extends StatefulWidget { - const RegionBottomSheetBody({super.key}); @override @@ -27,7 +24,11 @@ class _RegionBottomSheetBodyState extends State { @override void initState() { scheduleMicrotask(() { - myAppointmentsViewModel.getRegionMappedProjectList(); + if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_REGION) { + myAppointmentsViewModel.getRegionMappedProjectList(); + } else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_CLINIIC) { + myAppointmentsViewModel.getMappedDoctors(); + } }); super.initState(); } @@ -53,26 +54,19 @@ class _RegionBottomSheetBodyState extends State { return SizedBox( height: MediaQuery.of(context).size.height * 0.5, child: ListView.separated( - itemCount: - myAppointmentsVM.hospitalList?.registeredDoctorMap?.length ?? - 0, + itemCount: myAppointmentsVM.hospitalList?.registeredDoctorMap?.length ?? 0, separatorBuilder: (_, __) { return SizedBox( height: 16.h, ); }, itemBuilder: (_, index) { - String key = myAppointmentsVM - .hospitalList?.registeredDoctorMap?.keys - .toList()[index] ?? - ''; + String key = myAppointmentsVM.hospitalList?.registeredDoctorMap?.keys.toList()[index] ?? ''; return RegionListItem( title: key, subTitle: "", - hmcCount: - "${myAppointmentsVM.hospitalList?.registeredDoctorMap?[key]?.hmcSize ?? 0}", - hmgCount: - "${myAppointmentsVM.hospitalList?.registeredDoctorMap?[key]?.hmgSize ?? 0}", + hmcCount: "${myAppointmentsVM.hospitalList?.registeredDoctorMap?[key]?.hmcSize ?? 0}", + hmgCount: "${myAppointmentsVM.hospitalList?.registeredDoctorMap?[key]?.hmgSize ?? 0}", ).onPress(() { regionalViewModel.setSelectedRegionId(key); regionalViewModel.setBottomSheetState(AppointmentViaRegionState.TYPE_SELECTION); diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index 140b593..6ba14f5 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -17,7 +17,7 @@ import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_s import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart' show RegionBottomSheetBody; import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_by_name.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; @@ -42,6 +42,7 @@ class _BookAppointmentPageState extends State { @override void initState() { scheduleMicrotask(() { + bookAppointmentsViewModel.selectedTabIndex = 0; bookAppointmentsViewModel.initBookAppointmentViewModel(); bookAppointmentsViewModel.getLocation(); }); @@ -57,7 +58,7 @@ class _BookAppointmentPageState extends State { backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( title: LocaleKeys.bookAppo.tr(context: context), - isLeading: Navigator.canPop(context), + isLeading: Navigator.of(context).canPop(), child: SingleChildScrollView( child: Consumer(builder: (context, bookAppointmentsVM, child) { return Column( @@ -123,6 +124,7 @@ class _BookAppointmentPageState extends State { ).onPress(() { bookAppointmentsViewModel.setIsClinicsListLoading(true); bookAppointmentsViewModel.setLoadSpecificClinic(false); + bookAppointmentsViewModel.setIsLiveCareSchedule(false); bookAppointmentsViewModel.setProjectID(null); Navigator.of(context).push( CustomPageRoute( @@ -154,6 +156,7 @@ class _BookAppointmentPageState extends State { ], ).onPress(() { bookAppointmentsViewModel.setIsDoctorSearchByNameStarted(false); + bookAppointmentsViewModel.setProjectID(null); Navigator.of(context).push( CustomPageRoute( page: SearchDoctorByName(), @@ -183,7 +186,114 @@ class _BookAppointmentPageState extends State { flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h)), ], ).onPress(() { - openRegionListBottomSheet(context); + bookAppointmentsViewModel.setProjectID(null); + openRegionListBottomSheet(context, RegionBottomSheetType.FOR_REGION); + }), + ], + ), + ), + ), + ], + ).paddingSymmetrical(24.h, 0.h); + case 1: + //TODO: Get LiveCare type Select UI from Hussain + return Column( + children: [ + 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( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.search_by_clinic_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Immediate Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), + "Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + ], + ), + ], + ), + Transform.flip( + flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h)), + ], + ).onPress(() { + // bookAppointmentsViewModel.setIsClinicsListLoading(true); + // bookAppointmentsViewModel.setLoadSpecificClinic(false); + // bookAppointmentsViewModel.setProjectID(null); + // Navigator.of(context).push( + // CustomPageRoute( + // page: SelectClinicPage(), + // ), + // ); + }), + SizedBox(height: 16.h), + Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), + SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.search_by_doctor_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Scheduled Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), + "Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + ], + ), + ], + ), + Transform.flip( + flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h)), + ], + ).onPress(() { + bookAppointmentsViewModel.setIsClinicsListLoading(true); + bookAppointmentsViewModel.setIsLiveCareSchedule(true); + Navigator.of(context).push( + CustomPageRoute( + page: SelectClinicPage(), + ), + ); + }), + SizedBox(height: 16.h), + Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), + SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.search_by_region_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Pharma LiveCare".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), + "".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + ], + ), + ], + ), + Transform.flip( + flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h)), + ], + ).onPress(() { + openRegionListBottomSheet(context, RegionBottomSheetType.FOR_REGION); }), ], ), @@ -197,14 +307,11 @@ class _BookAppointmentPageState extends State { return Container(); } - void openRegionListBottomSheet(BuildContext context) { + void openRegionListBottomSheet(BuildContext context, RegionBottomSheetType type) { regionalViewModel.flush(); + regionalViewModel.setBottomSheetType(type); // AppointmentViaRegionViewmodel? viewmodel = null; - showCommonBottomSheetWithoutHeight(context, - title: "", - titleWidget: Consumer( - builder: (_, data, __) => getTitle(data)), - isDismissible: false, + showCommonBottomSheetWithoutHeight(context, title: "", titleWidget: Consumer(builder: (_, data, __) => getTitle(data)), isDismissible: false, child: Consumer(builder: (_, data, __) { return getRegionalSelectionWidget(data); }), callBackFunc: () {}); @@ -238,19 +345,16 @@ class _BookAppointmentPageState extends State { if (data.selectedRegionId == null) { return LocaleKeys.selectRegion.tr().toText20(weight: FontWeight.w600); } else { - return - Transform.flip( - flipX: data.isArabic ? true : false, - child: Utils.buildSvgWithAssets( - icon: AppAssets.arrow_back, - iconColor: Color(0xff2B353E), - - fit: BoxFit.contain, - ), - ).onPress(() { - data.handleBackPress(); - }); - + return Transform.flip( + flipX: data.isArabic ? true : false, + child: Utils.buildSvgWithAssets( + icon: AppAssets.arrow_back, + iconColor: Color(0xff2B353E), + fit: BoxFit.contain, + ), + ).onPress(() { + data.handleBackPress(); + }); } } } diff --git a/lib/presentation/book_appointment/doctor_profile_page.dart b/lib/presentation/book_appointment/doctor_profile_page.dart index 8640c79..6084c3d 100644 --- a/lib/presentation/book_appointment/doctor_profile_page.dart +++ b/lib/presentation/book_appointment/doctor_profile_page.dart @@ -14,7 +14,7 @@ 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/widgets/appbar/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'; @@ -115,29 +115,53 @@ class DoctorProfilePage extends StatelessWidget { 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, - ); - }); + bookAppointmentsViewModel.isLiveCareSchedule + ? await bookAppointmentsViewModel.getLiveCareDoctorFreeSlots( + isBookingForLiveCare: true, + 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, + ); + }) + : 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, diff --git a/lib/presentation/book_appointment/review_appointment_page.dart b/lib/presentation/book_appointment/review_appointment_page.dart index d3804d8..af3d354 100644 --- a/lib/presentation/book_appointment/review_appointment_page.dart +++ b/lib/presentation/book_appointment/review_appointment_page.dart @@ -10,14 +10,14 @@ 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/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/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/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/routes/custom_page_route.dart'; -import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; class ReviewAppointmentPage extends StatefulWidget { @@ -31,10 +31,12 @@ class _ReviewAppointmentPageState extends State { late AppState appState; late BookAppointmentsViewModel bookAppointmentsViewModel; late AuthenticationViewModel authVM; + late MyAppointmentsViewModel myAppointmentsViewModel; @override Widget build(BuildContext context) { bookAppointmentsViewModel = Provider.of(context, listen: false); + myAppointmentsViewModel = Provider.of(context, listen: false); authVM = Provider.of(context, listen: false); appState = getIt.get(); return Scaffold( @@ -203,35 +205,48 @@ class _ReviewAppointmentPageState extends State { void initiateBookAppointment() async { LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation); + myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); - 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, - CustomPageRoute( - page: LandingNavigation(), - ), - (r) => false); + if (bookAppointmentsViewModel.isLiveCareSchedule) { + await bookAppointmentsViewModel.insertSpecificAppointmentForLiveCare(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(); + bookAppointmentsViewModel.setIsLiveCareSchedule(false); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + 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(); - // }); - // }); - // }); + } else { + 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(); + bookAppointmentsViewModel.setIsLiveCareSchedule(false); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }); + }); + }); + } } } diff --git a/lib/presentation/book_appointment/search_doctor_by_name.dart b/lib/presentation/book_appointment/search_doctor_by_name.dart index e6b0ba1..8f611dc 100644 --- a/lib/presentation/book_appointment/search_doctor_by_name.dart +++ b/lib/presentation/book_appointment/search_doctor_by_name.dart @@ -12,7 +12,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/book_appointments 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/widgets/appbar/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'; diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart index 0580480..36a490d 100644 --- a/lib/presentation/book_appointment/select_clinic_page.dart +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -12,19 +12,27 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_livecare_clinics_response_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/select_livecare_clinic_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/clinic_card.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/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/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; class SelectClinicPage extends StatefulWidget { - const SelectClinicPage({super.key}); + bool isFromRegionFlow; + + SelectClinicPage({super.key, this.isFromRegionFlow = false}); @override State createState() => _SelectClinicPageState(); @@ -33,7 +41,7 @@ class SelectClinicPage extends StatefulWidget { class _SelectClinicPageState extends State { TextEditingController searchEditingController = TextEditingController(); FocusNode textFocusNode = FocusNode(); - + late AppointmentViaRegionViewmodel regionalViewModel; late AppState appState; late BookAppointmentsViewModel bookAppointmentsViewModel; @@ -54,11 +62,12 @@ class _SelectClinicPageState extends State { @override Widget build(BuildContext context) { bookAppointmentsViewModel = Provider.of(context, listen: false); + regionalViewModel = Provider.of(context, listen: true); appState = getIt.get(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( - title: LocaleKeys.selectClinic.tr(context: context), + title: bookAppointmentsViewModel.isLiveCareSchedule ? "Select LiveCare Clinic".needTranslation : LocaleKeys.selectClinic.tr(context: context), child: SingleChildScrollView( child: Padding( padding: EdgeInsets.symmetric(horizontal: 24.h), @@ -94,40 +103,83 @@ class _SelectClinicPageState extends State { horizontal: ResponsiveExtension(15).h, ), ), - ListView.separated( - padding: EdgeInsets.only(top: 24.h), - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemCount: bookAppointmentsVM.isClinicsListLoading ? 5 : bookAppointmentsVM.filteredClinicsList.length, - itemBuilder: (context, index) { - return bookAppointmentsVM.isClinicsListLoading - ? ClinicCard( - clinicsListResponseModel: GetClinicsListResponseModel(), - isLoading: bookAppointmentsVM.isClinicsListLoading, - ) - : 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: ClinicCard( - clinicsListResponseModel: bookAppointmentsVM.filteredClinicsList[index], - isLoading: bookAppointmentsVM.isClinicsListLoading, - ).onPress(() { - onClinicSelected(bookAppointmentsVM.filteredClinicsList[index]); - }), - ), - ), - ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), - ), + bookAppointmentsVM.isLiveCareSchedule + ? ListView.separated( + padding: EdgeInsets.only(top: 24.h), + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: bookAppointmentsVM.isClinicsListLoading ? 5 : bookAppointmentsVM.liveCareClinicsList.length, + itemBuilder: (context, index) { + return bookAppointmentsVM.isClinicsListLoading + ? ClinicCard( + bookAppointmentsVM: bookAppointmentsVM, + liveCareClinicsResponseModel: GetLiveCareClinicsResponseModel(), + clinicsListResponseModel: GetClinicsListResponseModel(), + isLoading: bookAppointmentsVM.isClinicsListLoading, + ) + : 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: ClinicCard( + bookAppointmentsVM: bookAppointmentsVM, + liveCareClinicsResponseModel: bookAppointmentsVM.liveCareClinicsList[index], + clinicsListResponseModel: GetClinicsListResponseModel(), + isLoading: bookAppointmentsVM.isClinicsListLoading, + ).onPress(() { + onLiveCareClinicSelected(bookAppointmentsVM.liveCareClinicsList[index]); + }), + ), + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), + ) + : ListView.separated( + padding: EdgeInsets.only(top: 24.h), + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: bookAppointmentsVM.isClinicsListLoading ? 5 : bookAppointmentsVM.filteredClinicsList.length, + itemBuilder: (context, index) { + return bookAppointmentsVM.isClinicsListLoading + ? ClinicCard( + bookAppointmentsVM: bookAppointmentsVM, + liveCareClinicsResponseModel: GetLiveCareClinicsResponseModel(), + clinicsListResponseModel: GetClinicsListResponseModel(), + isLoading: bookAppointmentsVM.isClinicsListLoading, + ) + : 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: ClinicCard( + bookAppointmentsVM: bookAppointmentsVM, + liveCareClinicsResponseModel: GetLiveCareClinicsResponseModel(), + clinicsListResponseModel: bookAppointmentsVM.filteredClinicsList[index], + isLoading: bookAppointmentsVM.isClinicsListLoading, + ).onPress(() { + onClinicSelected(bookAppointmentsVM.filteredClinicsList[index]); + }), + ), + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), + ), ], ); }), @@ -137,21 +189,92 @@ class _SelectClinicPageState extends State { ); } + void onLiveCareClinicSelected(GetLiveCareClinicsResponseModel clinic) { + bookAppointmentsViewModel.setLiveCareSelectedClinic(clinic); + bookAppointmentsViewModel.setIsDoctorsListLoading(true); + Navigator.of(context).push( + CustomPageRoute( + page: SelectDoctorPage(), + ), + ); + } + void onClinicSelected(GetClinicsListResponseModel clinic) { bookAppointmentsViewModel.setSelectedClinic(clinic); bookAppointmentsViewModel.setIsDoctorsListLoading(true); if (clinic.isLiveCareClinicAndOnline ?? false) { Navigator.of(context).push( CustomPageRoute( - page: SelectLivecareClinicPage(), + page: SelectLivecareClinicPage(onNegativeClicked: (){ + handleDoctorScreen(); + },), ), ); } else { + handleDoctorScreen(); + } + } + + void handleDoctorScreen() { + if (widget.isFromRegionFlow) { Navigator.of(context).push( CustomPageRoute( page: SelectDoctorPage(), ), ); + } else { + openRegionListBottomSheet(context, RegionBottomSheetType.FOR_CLINIIC); + } + } + + void openRegionListBottomSheet(BuildContext context, RegionBottomSheetType type) { + bookAppointmentsViewModel.setProjectID(null); + + regionalViewModel.flush(); + regionalViewModel.setBottomSheetType(type); + // AppointmentViaRegionViewmodel? viewmodel = null; + showCommonBottomSheetWithoutHeight(context, title: "", titleWidget: Consumer(builder: (_, data, __) => getTitle(data)), isDismissible: false, + child: Consumer(builder: (_, data, __) { + return getRegionalSelectionWidget(data); + }), callBackFunc: () {}); + } + + Widget getRegionalSelectionWidget(AppointmentViaRegionViewmodel data) { + if (data.bottomSheetState == AppointmentViaRegionState.REGION_SELECTION) { + return RegionBottomSheetBody(); + } + if (data.bottomSheetState == AppointmentViaRegionState.TYPE_SELECTION) { + bookAppointmentsViewModel.resetFilterList(); + return FacilityTypeSelectionWidget( + selectedRegion: data.selectedRegionId ?? "", + ); + } + if (data.bottomSheetState == AppointmentViaRegionState.HOSPITAL_SELECTION) { + return HospitalBottomSheetBody(); + } + if(data.bottomSheetState == AppointmentViaRegionState.DOCTOR_SELECTION){ + bookAppointmentsViewModel.setProjectID(regionalViewModel.selectedHospital?.patientDoctorAppointmentList?.first.projectID.toString()); + } + else { + return SizedBox.shrink(); + } + return SizedBox.shrink(); + } + + getTitle(AppointmentViaRegionViewmodel data) { + if (data.selectedRegionId == null) { + return LocaleKeys.selectRegion.tr().toText20(weight: FontWeight.w600); + } else { + return Transform.flip( + flipX: data.isArabic ? true : false, + child: Utils.buildSvgWithAssets( + icon: AppAssets.arrow_back, + iconColor: Color(0xff2B353E), + fit: BoxFit.contain, + ), + ).onPress(() { + data.handleBackPress(); + }); } } } diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart index 27a60f6..fd6e691 100644 --- a/lib/presentation/book_appointment/select_doctor_page.dart +++ b/lib/presentation/book_appointment/select_doctor_page.dart @@ -15,7 +15,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/models/resp_model 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/widgets/appbar/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'; @@ -42,7 +42,11 @@ class _SelectDoctorPageState extends State { @override void initState() { scheduleMicrotask(() { - bookAppointmentsViewModel.getDoctorsList(); + if (bookAppointmentsViewModel.isLiveCareSchedule) { + bookAppointmentsViewModel.getLiveCareDoctorsList(); + } else { + bookAppointmentsViewModel.getDoctorsList(); + } }); super.initState(); } @@ -96,7 +100,8 @@ class _SelectDoctorPageState extends State { padding: EdgeInsets.only(top: 24.h), shrinkWrap: true, physics: NeverScrollableScrollPhysics(), - itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : bookAppointmentsVM.doctorsList.length, + itemCount: + bookAppointmentsVM.isDoctorsListLoading ? 5 : (bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList.length : bookAppointmentsVM.doctorsList.length), itemBuilder: (context, index) { return bookAppointmentsVM.isDoctorsListLoading ? DoctorCard( @@ -115,11 +120,12 @@ class _SelectDoctorPageState extends State { curve: Curves.easeInOut, decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), child: DoctorCard( - doctorsListResponseModel: bookAppointmentsVM.doctorsList[index], + doctorsListResponseModel: bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index], isLoading: false, bookAppointmentsViewModel: bookAppointmentsViewModel, ).onPress(() async { - bookAppointmentsVM.setSelectedDoctor(bookAppointmentsVM.doctorsList[index]); + bookAppointmentsVM + .setSelectedDoctor(bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index]); // bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel()); LoaderBottomSheet.showLoader(); await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) { diff --git a/lib/presentation/book_appointment/select_livecare_clinic_page.dart b/lib/presentation/book_appointment/select_livecare_clinic_page.dart index 76d85af..1e40357 100644 --- a/lib/presentation/book_appointment/select_livecare_clinic_page.dart +++ b/lib/presentation/book_appointment/select_livecare_clinic_page.dart @@ -7,14 +7,16 @@ 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/widgets/appbar/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/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; class SelectLivecareClinicPage extends StatelessWidget { - const SelectLivecareClinicPage({super.key}); + + final VoidCallback? onNegativeClicked; + const SelectLivecareClinicPage({super.key, this.onNegativeClicked}); @override Widget build(BuildContext context) { @@ -122,11 +124,12 @@ class SelectLivecareClinicPage extends StatelessWidget { text: "No, Thanks. I would like a physical visit".needTranslation, onPressed: () { Navigator.of(context).pop(); - Navigator.of(context).push( - CustomPageRoute( - page: SelectDoctorPage(), - ), - ); + onNegativeClicked?.call(); + // Navigator.of(context).push( + // CustomPageRoute( + // page: SelectDoctorPage(), + // ), + // ); }, backgroundColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor, diff --git a/lib/presentation/book_appointment/widgets/appointment_calendar.dart b/lib/presentation/book_appointment/widgets/appointment_calendar.dart index 046003a..e7a47aa 100644 --- a/lib/presentation/book_appointment/widgets/appointment_calendar.dart +++ b/lib/presentation/book_appointment/widgets/appointment_calendar.dart @@ -59,8 +59,8 @@ class _AppointmentCalendarState extends State { @override void initState() { + _calendarController = CalendarController(); scheduleMicrotask(() { - _calendarController = CalendarController(); _events = { _selectedDay: ['Event A0'] }; diff --git a/lib/presentation/book_appointment/widgets/clinic_card.dart b/lib/presentation/book_appointment/widgets/clinic_card.dart index 57315e6..6850f9f 100644 --- a/lib/presentation/book_appointment/widgets/clinic_card.dart +++ b/lib/presentation/book_appointment/widgets/clinic_card.dart @@ -6,14 +6,18 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_livecare_clinics_response_model.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; class ClinicCard extends StatelessWidget { - ClinicCard({super.key, required this.clinicsListResponseModel, required this.isLoading}); + ClinicCard({super.key, required this.clinicsListResponseModel, required this.liveCareClinicsResponseModel, required this.isLoading, required this.bookAppointmentsVM}); GetClinicsListResponseModel clinicsListResponseModel; + GetLiveCareClinicsResponseModel liveCareClinicsResponseModel; bool isLoading; + BookAppointmentsViewModel bookAppointmentsVM; @override Widget build(BuildContext context) { @@ -28,14 +32,17 @@ class ClinicCard extends StatelessWidget { child: Column( children: [ Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - "".toText16(isBold: true).toShimmer2(isShow: isLoading), + Utils.buildSvgWithAssets(icon: AppAssets.generic_clinic_icon, width: 24.h, height: 24.h, fit: BoxFit.contain).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)), + Expanded( + child: (isLoading ? "Cardiology" : (bookAppointmentsVM.isLiveCareSchedule ? liveCareClinicsResponseModel.clinicDesc! : clinicsListResponseModel.clinicDescription!)) + .toText16(isBold: true) + .toShimmer2(isShow: isLoading)), Transform.flip( flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor).toShimmer2(isShow: isLoading)), diff --git a/lib/presentation/book_appointment/widgets/doctor_card.dart b/lib/presentation/book_appointment/widgets/doctor_card.dart index 1e1e0ae..3f3d51c 100644 --- a/lib/presentation/book_appointment/widgets/doctor_card.dart +++ b/lib/presentation/book_appointment/widgets/doctor_card.dart @@ -108,29 +108,53 @@ class DoctorCard extends StatelessWidget { 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, - ); - }); + bookAppointmentsViewModel.isLiveCareSchedule + ? await bookAppointmentsViewModel.getLiveCareDoctorFreeSlots( + isBookingForLiveCare: true, + 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, + ); + }) + : 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), diff --git a/lib/presentation/habib_wallet/habib_wallet_page.dart b/lib/presentation/habib_wallet/habib_wallet_page.dart index 2e909d2..a7ec23d 100644 --- a/lib/presentation/habib_wallet/habib_wallet_page.dart +++ b/lib/presentation/habib_wallet/habib_wallet_page.dart @@ -10,7 +10,7 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_page.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/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/routes/custom_page_route.dart'; diff --git a/lib/presentation/habib_wallet/recharge_wallet_page.dart b/lib/presentation/habib_wallet/recharge_wallet_page.dart index 0cd2d69..74eebec 100644 --- a/lib/presentation/habib_wallet/recharge_wallet_page.dart +++ b/lib/presentation/habib_wallet/recharge_wallet_page.dart @@ -13,7 +13,7 @@ import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_mode import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/wallet_payment_confirm_page.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/widgets/select_hospital_bottom_sheet.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/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'; diff --git a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart index 115557f..63aa313 100644 --- a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart +++ b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart @@ -17,7 +17,7 @@ import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_mode import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart'; import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; diff --git a/lib/presentation/habib_wallet/widgets/select_hospital_bottom_sheet.dart b/lib/presentation/habib_wallet/widgets/select_hospital_bottom_sheet.dart index 54c6fb3..b7a16c5 100644 --- a/lib/presentation/habib_wallet/widgets/select_hospital_bottom_sheet.dart +++ b/lib/presentation/habib_wallet/widgets/select_hospital_bottom_sheet.dart @@ -13,7 +13,7 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/widgets/hospital_list_item.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:provider/provider.dart'; diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 65f5c11..cb2f76f 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -180,7 +180,7 @@ class _LandingPageState extends State { ), ); }), - SizedBox(height: 12.h), + SizedBox(height: 16.h), Consumer(builder: (context, myAppointmentsVM, child) { return myAppointmentsVM.isMyAppointmentsLoading ? Container( @@ -211,7 +211,7 @@ class _LandingPageState extends State { : 3, layout: SwiperLayout.STACK, loop: true, - itemWidth: MediaQuery.of(context).size.width - 72, + itemWidth: MediaQuery.of(context).size.width - 48.h, indicatorLayout: PageIndicatorLayout.COLOR, axisDirection: AxisDirection.right, controller: _controller, @@ -289,49 +289,46 @@ class _LandingPageState extends State { ), ); }), - SizedBox(height: 12.h), + SizedBox(height: 16.h), Container( - height: 127.h, + height: 120.h, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 24, ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - children: [ - Expanded( - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: LandingPageData.getLoggedInServiceCardsList.length, - shrinkWrap: true, - padding: const EdgeInsets.only(left: 0, right: 8), - itemBuilder: (context, index) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 1000), - child: SlideAnimation( - horizontalOffset: 100.0, - child: FadeInAnimation( - child: SmallServiceCard( - icon: LandingPageData.getLoggedInServiceCardsList[index].icon, - title: LandingPageData.getLoggedInServiceCardsList[index].title, - subtitle: LandingPageData.getLoggedInServiceCardsList[index].subtitle, - iconColor: LandingPageData.getLoggedInServiceCardsList[index].iconColor, - textColor: LandingPageData.getLoggedInServiceCardsList[index].textColor, - backgroundColor: LandingPageData.getLoggedInServiceCardsList[index].backgroundColor, - isBold: LandingPageData.getLoggedInServiceCardsList[index].isBold, - serviceName: LandingPageData.getLoggedInServiceCardsList[index].serviceName, - ), + child: Column( + children: [ + Expanded( + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: LandingPageData.getLoggedInServiceCardsList.length, + shrinkWrap: true, + padding: EdgeInsets.only(left: 16.h, right: 16.h), + itemBuilder: (context, index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + horizontalOffset: 100.0, + child: FadeInAnimation( + child: SmallServiceCard( + icon: LandingPageData.getLoggedInServiceCardsList[index].icon, + title: LandingPageData.getLoggedInServiceCardsList[index].title, + subtitle: LandingPageData.getLoggedInServiceCardsList[index].subtitle, + iconColor: LandingPageData.getLoggedInServiceCardsList[index].iconColor, + textColor: LandingPageData.getLoggedInServiceCardsList[index].textColor, + backgroundColor: LandingPageData.getLoggedInServiceCardsList[index].backgroundColor, + isBold: LandingPageData.getLoggedInServiceCardsList[index].isBold, + serviceName: LandingPageData.getLoggedInServiceCardsList[index].serviceName, ), ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 0.width, - ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => 0.width, ), - ], - ), + ), + ], ), ).paddingSymmetrical(24.h, 0.h), ], @@ -342,41 +339,38 @@ class _LandingPageState extends State { color: AppColors.whiteColor, borderRadius: 24, ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - children: [ - Expanded( - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: LandingPageData.getNotLoggedInServiceCardsList.length, - shrinkWrap: true, - padding: const EdgeInsets.only(left: 0, right: 8), - itemBuilder: (context, index) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 1000), - child: SlideAnimation( - horizontalOffset: 100.0, - child: FadeInAnimation( - child: SmallServiceCard( - icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon, - title: LandingPageData.getNotLoggedInServiceCardsList[index].title, - subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle, - iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor, - textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor, - backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor, - isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold, - ), + child: Column( + children: [ + Expanded( + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: LandingPageData.getNotLoggedInServiceCardsList.length, + shrinkWrap: true, + padding: EdgeInsets.only(left: 16.h, right: 16.h), + itemBuilder: (context, index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + horizontalOffset: 100.0, + child: FadeInAnimation( + child: SmallServiceCard( + icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon, + title: LandingPageData.getNotLoggedInServiceCardsList[index].title, + subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle, + iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor, + textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor, + backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor, + isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold, ), ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 0.width, - ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => 0.width, ), - ], - ), + ), + ], ), ).paddingSymmetrical(24.h, 0.h), Row( diff --git a/lib/presentation/home/navigation_screen.dart b/lib/presentation/home/navigation_screen.dart index 8feeb5f..6b33b2f 100644 --- a/lib/presentation/home/navigation_screen.dart +++ b/lib/presentation/home/navigation_screen.dart @@ -34,6 +34,7 @@ class _LandingNavigationState extends State { ], ), bottomNavigationBar: BottomNavigation( + context: context, currentIndex: _currentIndex, onTap: (index) { setState(() => _currentIndex = index); diff --git a/lib/presentation/home/widgets/small_service_card.dart b/lib/presentation/home/widgets/small_service_card.dart index fba96eb..416ed9a 100644 --- a/lib/presentation/home/widgets/small_service_card.dart +++ b/lib/presentation/home/widgets/small_service_card.dart @@ -39,7 +39,7 @@ class SmallServiceCard extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: EdgeInsets.symmetric(horizontal: 3.h), + padding: EdgeInsets.symmetric(horizontal: 0.h, vertical: 12.h), child: Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: backgroundColor, diff --git a/lib/presentation/insurance/insurance_home_page.dart b/lib/presentation/insurance/insurance_home_page.dart index dd63e55..41640b7 100644 --- a/lib/presentation/insurance/insurance_home_page.dart +++ b/lib/presentation/insurance/insurance_home_page.dart @@ -9,7 +9,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/insurance/widgets/patient_insurance_card.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart index 8657534..6fd8234 100644 --- a/lib/presentation/lab/lab_orders_page.dart +++ b/lib/presentation/lab/lab_orders_page.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/lab/lab_range_view_model.dart'; @@ -20,7 +21,7 @@ import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:provider/provider.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; -import 'collapsing_list_view.dart'; +import '../../widgets/appbar/collapsing_list_view.dart'; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @@ -37,6 +38,7 @@ class _LabOrdersPageState extends State { int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; + @override void initState() { scheduleMicrotask(() { @@ -90,10 +92,8 @@ class _LabOrdersPageState extends State { // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { - activeIndex = index; - setState(() { - - }); + activeIndex = index; + setState(() {}); }, ), SizedBox(height: 16.h), @@ -109,7 +109,11 @@ class _LabOrdersPageState extends State { shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, - itemCount: model.isLabOrdersLoading ? 5 : model.patientLabOrders.length, + itemCount: model.isLabOrdersLoading + ? 5 + : model.patientLabOrders.isNotEmpty + ? model.patientLabOrders.length + : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading @@ -119,39 +123,42 @@ class _LabOrdersPageState extends State { index: index, isLoading: true, ) - : AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 500), - child: SlideAnimation( - verticalOffset: 100.0, - child: FadeInAnimation( - child: LabResultItemView( - onTap: () { - setState(() { - expandedIndex = isExpanded ? null : index; - }); - }, - labOrder: model.patientLabOrders[index], - index: index, - isExpanded: isExpanded)), - ), - ); + : model.patientLabOrders.isNotEmpty + ? AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: LabResultItemView( + onTap: () { + setState(() { + expandedIndex = isExpanded ? null : index; + }); + }, + labOrder: model.patientLabOrders[index], + index: index, + isExpanded: isExpanded)), + ), + ) + : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) : ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, - itemCount: model.isLabOrdersLoading ? 5 :model.uniqueTests.toList().length, + itemCount: model.isLabOrdersLoading ? 5 : model.uniqueTests.toList().length, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( - onTap: () {}, - labOrder: null, - index: index, - isLoading: true, - ) : AnimationConfiguration.staggeredList( + onTap: () {}, + labOrder: null, + index: index, + isLoading: true, + ) + : AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( @@ -173,7 +180,6 @@ class _LabOrdersPageState extends State { isExpanded: isExpanded)), ), ); - }, ) ], diff --git a/lib/presentation/lab/lab_results/lab_result_details.dart b/lib/presentation/lab/lab_results/lab_result_details.dart index 402e30e..3e9558c 100644 --- a/lib/presentation/lab/lab_results/lab_result_details.dart +++ b/lib/presentation/lab/lab_results/lab_result_details.dart @@ -1,26 +1,23 @@ -import 'package:dartz/dartz.dart'; + +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/common_models/data_points.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/lab/history/lab_history_viewmodel.dart'; import 'package:hmg_patient_app_new/features/lab/lab_range_view_model.dart' show LabRangeViewModel; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/lab_result.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_calender.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_list_item.dart'; import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/graph/custom_graph.dart'; import 'package:provider/provider.dart' show Consumer, Provider; import '../../../widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight; -import '../../book_appointment/widgets/appointment_calendar.dart' - show AppointmentCalendar; class LabResultDetails extends StatelessWidget { final LabResult recentLabResult; @@ -62,6 +59,7 @@ class LabResultDetails extends StatelessWidget { recentLabResult.testCode ?? "", style: TextStyle( fontSize: 32, + fontFamily: 'Poppins', fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -2), @@ -70,6 +68,7 @@ class LabResultDetails extends StatelessWidget { "Result of ${recentLabResult.verifiedOn ?? ""}".needTranslation, style: TextStyle( fontSize: 12, + fontFamily: 'Poppins', fontWeight: FontWeight.w500, color: AppColors.greyTextColor, ), @@ -91,6 +90,7 @@ class LabResultDetails extends StatelessWidget { style: TextStyle( fontSize: 24.fSize, fontWeight: FontWeight.w600, + fontFamily: 'Poppins', color: model.getColor( recentLabResult.calculatedResultFlag ?? "", ), @@ -108,6 +108,7 @@ class LabResultDetails extends StatelessWidget { style: TextStyle( fontSize: 12.fSize, fontWeight: FontWeight.w500, + fontFamily: 'Poppins', color: AppColors.greyTextColor, ), overflow: TextOverflow.ellipsis, @@ -153,6 +154,8 @@ class LabResultDetails extends StatelessWidget { model.isGraphVisible?"History FlowChart".needTranslation: "History".needTranslation, style: TextStyle( fontSize: 16, + fontFamily: 'Poppins', + fontWeight: FontWeight.w600, color: AppColors.textColor, ), @@ -202,7 +205,7 @@ class LabResultDetails extends StatelessWidget { return Text( value, style: TextStyle( - fontWeight: FontWeight.w300, + fontWeight: FontWeight.w600, fontFamily: 'Poppins', fontSize: 8.fSize, color: AppColors.textColor, @@ -226,26 +229,36 @@ class LabResultDetails extends StatelessWidget { Widget historyBody(LabRangeViewModel model, LabViewModel labmodel) { if(model.isGraphVisible){ + var graphColor = labmodel.getColor(recentLabResult.calculatedResultFlag??"N"); return CustomGraph( dataPoints: labmodel.filteredGraphValues, - maxY: 100, + // maxY: 100, + makeGraphBasedOnActualValue: true, + leftLabelReservedSize: 40, + leftLabelInterval: getInterval(labmodel), + maxY: (labmodel.maxY)+(getInterval(labmodel)??0)/2, + leftLabelFormatter: (value) { - switch (value.toInt()) { - case 20: - return leftLabels("Critical Low".needTranslation); - case 40: - return leftLabels("Low".needTranslation); - case 60: - return leftLabels("Normal".needTranslation); - case 80: - return leftLabels("High".needTranslation); - case 100: - return leftLabels( - "Critical High".needTranslation); - default: - return SizedBox.shrink(); - } + return leftLabels(value.toStringAsFixed(2).tr()); + // switch (value.toInt()) { + // case 10: + // return leftLabels("Critical Low".needTranslation); + // case 30: + // return leftLabels("Low".needTranslation); + // case 50: + // return leftLabels("Normal".needTranslation); + // case 70: + // return leftLabels("High".needTranslation); + // case 90: + // return leftLabels( + // "Critical High".needTranslation); + // default: + // return SizedBox.shrink(); + // } }, + graphColor:graphColor , + graphShadowColor: graphColor.withOpacity(.4), + graphGridColor: graphColor.withOpacity(.4), bottomLabelFormatter: (value, data) { if(data.isEmpty) return SizedBox.shrink(); if (value == 0) { @@ -259,6 +272,7 @@ class LabResultDetails extends StatelessWidget { } return SizedBox.shrink(); }, + minX:(labmodel.filteredGraphValues.length == 1)?null : -.2, scrollDirection: Axis.horizontal, height: 180.h); }else { @@ -276,8 +290,8 @@ class LabResultDetails extends StatelessWidget { return LabHistoryItem( dayNameAndDate: labmodel.getFormattedDate(data.time), result: data.actualValue, - assetUrl: labmodel.getAssetUrlWRTResult(data.refernceValue), - shouldRotateIcon: labmodel.getRotationWRTResult(data.refernceValue), + assetUrl: labmodel.getAssetUrlWRTResult(data.referenceValue), + shouldRotateIcon: labmodel.getRotationWRTResult(data.referenceValue), ); }, separatorBuilder: (_, __) => Divider( @@ -287,4 +301,14 @@ class LabResultDetails extends StatelessWidget { ), ); } + + double? getInterval(LabViewModel labmodel) { + var maxX = labmodel.maxY; + if(maxX >1 && maxX < 5) return 1; + if(maxX >5 && maxX < 10) return 5; + if(maxX >10 && maxX < 50) return 10; + if(maxX >50 && maxX < 100) return 20; + if(maxX >100 && maxX < 200) return 30; + return 50; + } } diff --git a/lib/presentation/lab/search_lab_report.dart b/lib/presentation/lab/search_lab_report.dart index 1b47a55..f1fd389 100644 --- a/lib/presentation/lab/search_lab_report.dart +++ b/lib/presentation/lab/search_lab_report.dart @@ -4,7 +4,7 @@ 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/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/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/input_widget.dart'; diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index dcd83c6..4b5259f 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -29,14 +29,14 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointme import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; import 'package:hmg_patient_app_new/presentation/insurance/widgets/patient_insurance_card.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/medical_reports_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/patient_sickleaves_list_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/vaccine_list_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/lab_rad_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_sick_leave_card.dart'; -import 'package:hmg_patient_app_new/presentation/my_family/my_Family.dart'; +import 'package:hmg_patient_app_new/presentation/my_family/my_family.dart'; import 'package:hmg_patient_app_new/presentation/my_family/widget/my_family_sheet.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; @@ -285,7 +285,7 @@ class _MedicalFilePageState extends State { }), Consumer(builder: (context, myAppointmentsVM, child) { return SizedBox( - height: 200.h, + height: myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty ? 200.h : 175.h, child: ListView.separated( scrollDirection: Axis.horizontal, padding: EdgeInsets.only(top: 16.h, left: 24.h, right: 24.h, bottom: 0.h), @@ -478,7 +478,7 @@ class _MedicalFilePageState extends State { backgroundColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, - fontSize: 14, + fontSize: 13, fontWeight: FontWeight.w500, borderRadius: 12.h, height: 40.h, @@ -495,7 +495,7 @@ class _MedicalFilePageState extends State { backgroundColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, - fontSize: 14, + fontSize: 13, fontWeight: FontWeight.w500, borderRadius: 12.h, height: 40.h, @@ -510,7 +510,7 @@ class _MedicalFilePageState extends State { ), ), ).paddingSymmetrical(24.h, 0.h) - : SizedBox.shrink(); + : Utils.getNoDataWidget(context, noDataText: "You don't have any prescriptions yet.".needTranslation, isSmallWidget: true, width: 62, height: 62).paddingSymmetrical(24.h, 0.h); }), SizedBox(height: 24.h), //My Doctor Section @@ -541,7 +541,11 @@ class _MedicalFilePageState extends State { height: 120.h, child: ListView.separated( scrollDirection: Axis.horizontal, - itemCount: myAppointmentsVM.isPatientMyDoctorsLoading ? 5 : myAppointmentsVM.patientMyDoctorsList.length, + itemCount: myAppointmentsVM.isPatientMyDoctorsLoading + ? 5 + : myAppointmentsVM.patientMyDoctorsList.isNotEmpty + ? myAppointmentsVM.patientMyDoctorsList.length + : 1, shrinkWrap: true, padding: EdgeInsets.only(left: 24.h, right: 24.h), itemBuilder: (context, index) { @@ -564,13 +568,14 @@ class _MedicalFilePageState extends State { ], ), ) - : AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 1000), - child: SlideAnimation( - horizontalOffset: 100.0, - child: FadeInAnimation( - child: SizedBox( + : myAppointmentsVM.patientMyDoctorsList.isNotEmpty + ? AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + horizontalOffset: 100.0, + child: FadeInAnimation( + child: SizedBox( width: 80.h, child: Column( crossAxisAlignment: CrossAxisAlignment.center, @@ -593,7 +598,9 @@ class _MedicalFilePageState extends State { ), ), ), - ); + ) + : Utils.getNoDataWidget(context, noDataText: "You don't have any completed visits yet.".needTranslation, isSmallWidget: true, width: 62, height: 62) + .paddingSymmetrical(24.h, 0.h); }, separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.h), ), diff --git a/lib/presentation/medical_file/medical_reports_page.dart b/lib/presentation/medical_file/medical_reports_page.dart index ea3c34d..9baf538 100644 --- a/lib/presentation/medical_file/medical_reports_page.dart +++ b/lib/presentation/medical_file/medical_reports_page.dart @@ -5,7 +5,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_medical_report_card.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; diff --git a/lib/presentation/medical_file/patient_sickleaves_list_page.dart b/lib/presentation/medical_file/patient_sickleaves_list_page.dart index 9950779..215836f 100644 --- a/lib/presentation/medical_file/patient_sickleaves_list_page.dart +++ b/lib/presentation/medical_file/patient_sickleaves_list_page.dart @@ -4,11 +4,13 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:provider/provider.dart'; @@ -50,7 +52,11 @@ class _PatientSickleavesListPageState extends State { children: [ ListView.separated( scrollDirection: Axis.vertical, - itemCount: medicalFileVM.isPatientSickLeaveListLoading ? 3 : medicalFileVM.patientSickLeaveList.length, + itemCount: medicalFileVM.isPatientSickLeaveListLoading + ? 3 + : medicalFileVM.patientSickLeaveList.isNotEmpty + ? medicalFileVM.patientSickLeaveList.length + : 1, shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemBuilder: (context, index) { @@ -73,7 +79,7 @@ class _PatientSickleavesListPageState extends State { ), ), ) - : SizedBox.shrink(); + : Utils.getNoDataWidget(context, noDataText: "You don't have any sick leaves yet.".needTranslation); }, separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 8.h), ), diff --git a/lib/presentation/medical_file/vaccine_list_page.dart b/lib/presentation/medical_file/vaccine_list_page.dart index 0cf48d6..969e3f5 100644 --- a/lib/presentation/medical_file/vaccine_list_page.dart +++ b/lib/presentation/medical_file/vaccine_list_page.dart @@ -8,7 +8,7 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:provider/provider.dart'; diff --git a/lib/presentation/medical_file/widgets/lab_rad_card.dart b/lib/presentation/medical_file/widgets/lab_rad_card.dart index 66021b2..766ef6d 100644 --- a/lib/presentation/medical_file/widgets/lab_rad_card.dart +++ b/lib/presentation/medical_file/widgets/lab_rad_card.dart @@ -32,7 +32,7 @@ class LabRadCard extends StatelessWidget { fit: BoxFit.contain, ).toShimmer2(isShow: false, radius: 12.h), SizedBox(width: 8.h), - labelText.toText14(isBold: true).toShimmer2(isShow: false, radius: 6.h, height: 32.h), + labelText.toText13(isBold: true).toShimmer2(isShow: false, radius: 6.h, height: 32.h), ], ), SizedBox(height: 16.h), diff --git a/lib/presentation/medical_file/widgets/patient_medical_report_card.dart b/lib/presentation/medical_file/widgets/patient_medical_report_card.dart index eb1730c..1762886 100644 --- a/lib/presentation/medical_file/widgets/patient_medical_report_card.dart +++ b/lib/presentation/medical_file/widgets/patient_medical_report_card.dart @@ -110,15 +110,15 @@ class PatientMedicalReportCard extends StatelessWidget { onPressed: () async { getMedicalReportPDF(false, context, _appState); }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, fontSize: 14, fontWeight: FontWeight.w500, borderRadius: 12.h, height: 40.h, icon: AppAssets.download_1, - iconColor: AppColors.primaryRedColor, + iconColor: AppColors.whiteColor, iconSize: 16.h, ).toShimmer2(isShow: isLoading), ), diff --git a/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart index ba1bee5..5cd2547 100644 --- a/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart +++ b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart @@ -43,7 +43,7 @@ class PatientSickLeaveCard extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - "${LocaleKeys.sick.tr(context: context)} ${LocaleKeys.sickSubtitle.tr(context: context)}".toText16(isBold: true), + "${LocaleKeys.sick.tr(context: context)} ${LocaleKeys.sickSubtitle.tr(context: context)}".toText16(isBold: true).toShimmer2(isShow: isLoading), AppCustomChipWidget( labelText: isLoading ? "" : getStatusText(context), backgroundColor: getStatusColor().withOpacity(0.15), diff --git a/lib/presentation/my_family/my_Family.dart b/lib/presentation/my_family/my_family.dart similarity index 97% rename from lib/presentation/my_family/my_Family.dart rename to lib/presentation/my_family/my_family.dart index c0d85c0..a2bcfaa 100644 --- a/lib/presentation/my_family/my_Family.dart +++ b/lib/presentation/my_family/my_family.dart @@ -1,11 +1,9 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; -import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; @@ -15,7 +13,6 @@ import 'package:hmg_patient_app_new/features/medical_file/models/family_file_res import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/presentation/my_family/widget/family_cards.dart'; -import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; @@ -153,9 +150,9 @@ class _FamilyMedicalScreenState extends State { } } - void showModelSheet() { + Future showModelSheet() async { AuthenticationViewModel authVm = getIt.get(); - return showCommonBottomSheetWithoutHeight(context, + return await showCommonBottomSheetWithoutHeight(context, title: "Add Family Member", useSafeArea: true, child: Column( diff --git a/lib/presentation/my_family/widget/my_family_sheet.dart b/lib/presentation/my_family/widget/my_family_sheet.dart index a119906..da822b1 100644 --- a/lib/presentation/my_family/widget/my_family_sheet.dart +++ b/lib/presentation/my_family/widget/my_family_sheet.dart @@ -1,15 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart'; -import 'package:hmg_patient_app_new/presentation/my_family/my_Family.dart'; import 'package:hmg_patient_app_new/presentation/my_family/widget/family_cards.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; class MyFamilySheet { - static void show(BuildContext context, List familyLists, Function(FamilyFileResponseModelLists) onSelect) { - return showCommonBottomSheetWithoutHeight( + static Future show(BuildContext context, List familyLists, Function(FamilyFileResponseModelLists) onSelect) async { + return await showCommonBottomSheetWithoutHeight( context, titleWidget: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/presentation/prescriptions/prescription_detail_page.dart b/lib/presentation/prescriptions/prescription_detail_page.dart index b3ace3d..9f6d55b 100644 --- a/lib/presentation/prescriptions/prescription_detail_page.dart +++ b/lib/presentation/prescriptions/prescription_detail_page.dart @@ -13,7 +13,7 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_item_view.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_reminder_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; diff --git a/lib/presentation/prescriptions/prescriptions_list_page.dart b/lib/presentation/prescriptions/prescriptions_list_page.dart index 3bca56d..87f880d 100644 --- a/lib/presentation/prescriptions/prescriptions_list_page.dart +++ b/lib/presentation/prescriptions/prescriptions_list_page.dart @@ -13,7 +13,8 @@ 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/prescriptions/prescriptions_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; @@ -93,20 +94,30 @@ class _PrescriptionsListPageState extends State { SizedBox(height: 20.h), // Expandable list ListView.builder( - itemCount: model.isPrescriptionsOrdersLoading ? 4 : model.patientPrescriptionOrdersViewList.length, + itemCount: model.isPrescriptionsOrdersLoading + ? 4 + : model.patientPrescriptionOrders.isNotEmpty + ? model.patientPrescriptionOrdersViewList.length + : 1, physics: NeverScrollableScrollPhysics(), shrinkWrap: true, padding: const EdgeInsets.only(left: 0, right: 8), itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isPrescriptionsOrdersLoading - ? const MoviesShimmerWidget() - : AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 500), - child: SlideAnimation( - verticalOffset: 100.0, - child: FadeInAnimation( + ? LabResultItemView( + onTap: () {}, + labOrder: null, + index: index, + isLoading: true, + ) + : model.patientPrescriptionOrders.isNotEmpty + ? AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( child: AnimatedContainer( duration: Duration(milliseconds: 300), curve: Curves.easeInOut, @@ -290,7 +301,8 @@ class _PrescriptionsListPageState extends State { ), ), ), - ); + ) + : Utils.getNoDataWidget(context, noDataText: "You don't have any prescriptions yet.".needTranslation); }, ).paddingSymmetrical(24.h, 0.h), ], diff --git a/lib/presentation/profile_settings/profile_settings.dart b/lib/presentation/profile_settings/profile_settings.dart index 4328a58..58273fc 100644 --- a/lib/presentation/profile_settings/profile_settings.dart +++ b/lib/presentation/profile_settings/profile_settings.dart @@ -11,7 +11,7 @@ import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_mode import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_page.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/app_language_change.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; diff --git a/lib/presentation/radiology/radiology_orders_page.dart b/lib/presentation/radiology/radiology_orders_page.dart index c7732a4..6662a8e 100644 --- a/lib/presentation/radiology/radiology_orders_page.dart +++ b/lib/presentation/radiology/radiology_orders_page.dart @@ -11,7 +11,8 @@ 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/features/lab/lab_view_model.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/presentation/radiology/radiology_result_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; @@ -61,130 +62,143 @@ class _RadiologyOrdersPageState extends State { ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), - itemCount: model.isRadiologyOrdersLoading ? 5 : model.patientRadiologyOrders.length, + itemCount: model.isRadiologyOrdersLoading + ? 5 + : model.patientRadiologyOrders.isNotEmpty + ? model.patientRadiologyOrders.length + : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 500), - child: SlideAnimation( - verticalOffset: 100.0, - child: FadeInAnimation( - child: AnimatedContainer( - duration: Duration(milliseconds: 300), - curve: Curves.easeInOut, - margin: EdgeInsets.symmetric(vertical: 8.h), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), - child: InkWell( - onTap: () { - setState(() { - expandedIndex = isExpanded ? null : index; - }); - }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.all(16.h), + return model.isRadiologyOrdersLoading + ? LabResultItemView( + onTap: () {}, + labOrder: null, + index: index, + isLoading: true, + ) + : model.patientRadiologyOrders.isNotEmpty + ? AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: AnimatedContainer( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + margin: EdgeInsets.symmetric(vertical: 8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), + child: InkWell( + onTap: () { + setState(() { + expandedIndex = isExpanded ? null : index; + }); + }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - AppCustomChipWidget( - labelText: LocaleKeys.resultsAvailable.tr(context: context), - backgroundColor: AppColors.successColor.withOpacity(0.15), - textColor: AppColors.successColor, - ).toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 100), - SizedBox(height: 8.h), - Row( - children: [ - Image.network( - model.isRadiologyOrdersLoading - ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" - : model.patientRadiologyOrders[index].doctorImageURL!, - width: 24.h, - height: 24.h, - fit: BoxFit.fill, - ).circle(100).toShimmer2(isShow: model.isRadiologyOrdersLoading), - SizedBox(width: 4.h), - (model.isRadiologyOrdersLoading ? "Dr John Smith" : model.patientRadiologyOrders[index].doctorName!) - .toText16(isBold: true) - .toShimmer2(isShow: model.isRadiologyOrdersLoading) - ], - ), - SizedBox(height: 8.h), - Wrap( - direction: Axis.horizontal, - spacing: 3.h, - runSpacing: 4.h, - children: [ - AppCustomChipWidget( - icon: AppAssets.doctor_calendar_icon, - labelText: model.isRadiologyOrdersLoading ? "01 Jan 2025" : DateUtil.formatDateToDate(model.patientRadiologyOrders[index].orderDate!, false), - ).toShimmer2(isShow: model.isRadiologyOrdersLoading), - AppCustomChipWidget( - labelText: model.isRadiologyOrdersLoading ? "01 Jan 2025" : model.patientRadiologyOrders[index].clinicDescription!, - ).toShimmer2(isShow: model.isRadiologyOrdersLoading), + Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AppCustomChipWidget( + labelText: LocaleKeys.resultsAvailable.tr(context: context), + backgroundColor: AppColors.successColor.withOpacity(0.15), + textColor: AppColors.successColor, + ).toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 100), + SizedBox(height: 8.h), + Row( + children: [ + Image.network( + model.isRadiologyOrdersLoading + ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" + : model.patientRadiologyOrders[index].doctorImageURL!, + width: 24.h, + height: 24.h, + fit: BoxFit.fill, + ).circle(100).toShimmer2(isShow: model.isRadiologyOrdersLoading), + SizedBox(width: 4.h), + (model.isRadiologyOrdersLoading ? "Dr John Smith" : model.patientRadiologyOrders[index].doctorName!) + .toText16(isBold: true) + .toShimmer2(isShow: model.isRadiologyOrdersLoading) + ], + ), + SizedBox(height: 8.h), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget( + icon: AppAssets.doctor_calendar_icon, + labelText: model.isRadiologyOrdersLoading ? "01 Jan 2025" : DateUtil.formatDateToDate(model.patientRadiologyOrders[index].orderDate!, false), + ).toShimmer2(isShow: model.isRadiologyOrdersLoading), + AppCustomChipWidget( + labelText: model.isRadiologyOrdersLoading ? "01 Jan 2025" : model.patientRadiologyOrders[index].clinicDescription!, + ).toShimmer2(isShow: model.isRadiologyOrdersLoading), - // AppCustomChipWidget(labelText: "").toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 16.h), - // AppCustomChipWidget(labelText: "").toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 16.h), - ], + // AppCustomChipWidget(labelText: "").toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 16.h), + // AppCustomChipWidget(labelText: "").toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 16.h), + ], + ), + ], + ), ), + model.isRadiologyOrdersLoading + ? SizedBox.shrink() + : AnimatedCrossFade( + firstChild: SizedBox.shrink(), + secondChild: Padding( + padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(bottom: 8.h), + child: '● ${model.patientRadiologyOrders[index].description}'.toText14(weight: FontWeight.w500), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox(), + CustomButton( + icon: AppAssets.view_report_icon, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + text: LocaleKeys.viewReport.tr(context: context), + onPressed: () { + Navigator.of(context).push( + CustomPageRoute( + page: RadiologyResultPage(patientRadiologyResponseModel: model.patientRadiologyOrders[index]), + ), + ); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14, + fontWeight: FontWeight.bold, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), + ], + ), + ], + ), + ), + crossFadeState: isExpanded ? CrossFadeState.showSecond : CrossFadeState.showFirst, + duration: Duration(milliseconds: 300), + ), ], ), ), - model.isRadiologyOrdersLoading - ? SizedBox.shrink() - : AnimatedCrossFade( - firstChild: SizedBox.shrink(), - secondChild: Padding( - padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only(bottom: 8.h), - child: '● ${model.patientRadiologyOrders[index].description}'.toText14(weight: FontWeight.w500), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - SizedBox(), - CustomButton( - icon: AppAssets.view_report_icon, - iconColor: AppColors.primaryRedColor, - iconSize: 16.h, - text: LocaleKeys.viewReport.tr(context: context), - onPressed: () { - Navigator.of(context).push( - CustomPageRoute( - page: RadiologyResultPage(patientRadiologyResponseModel: model.patientRadiologyOrders[index]), - ), - ); - }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: 14, - fontWeight: FontWeight.bold, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - ), - ], - ), - ], - ), - ), - crossFadeState: isExpanded ? CrossFadeState.showSecond : CrossFadeState.showFirst, - duration: Duration(milliseconds: 300), - ), - ], + ), ), ), - ), - ), - ), - ); + ) + : Utils.getNoDataWidget(context, noDataText: "You don't have any radiology results yet.".needTranslation); }, ), ], diff --git a/lib/presentation/radiology/radiology_result_page.dart b/lib/presentation/radiology/radiology_result_page.dart index f0f9af4..e15c8d6 100644 --- a/lib/presentation/radiology/radiology_result_page.dart +++ b/lib/presentation/radiology/radiology_result_page.dart @@ -11,7 +11,7 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/radiology/models/resp_models/patient_radiology_response_model.dart'; import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/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'; diff --git a/lib/services/dialog_service.dart b/lib/services/dialog_service.dart index 474a0ec..6ee0ddc 100644 --- a/lib/services/dialog_service.dart +++ b/lib/services/dialog_service.dart @@ -4,18 +4,15 @@ import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/extensions/route_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; -import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; -import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/bottomsheet/exception_bottom_sheet.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:provider/provider.dart'; abstract class DialogService { - Future showErrorBottomSheet({required String message, Function()? onOkPressed}); + Future showErrorBottomSheet({String title = "", required String message, Function()? onOkPressed, Function()? onCancelPressed}); Future showExceptionBottomSheet({required String message, required Function() onOkPressed, Function()? onCancelPressed}); @@ -28,21 +25,40 @@ abstract class DialogService { class DialogServiceImp implements DialogService { final NavigationService navigationService; + bool _isErrorSheetShowing = false; + DialogServiceImp({required this.navigationService}); @override - Future showErrorBottomSheet({required String message, Function()? onOkPressed}) async { + Future showErrorBottomSheet({String title = "", required String message, Function()? onOkPressed, Function()? onCancelPressed}) async { + if (_isErrorSheetShowing) return; final context = navigationService.navigatorKey.currentContext; if (context == null) return; + _isErrorSheetShowing = true; await showModalBottomSheet( context: context, isScrollControlled: false, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), - builder: (_) => _ErrorBottomSheet(message: message, onOkPressed: onOkPressed ?? () {}), + builder: (_) => ExceptionBottomSheet( + // title: title, + message: message, + showCancel: onCancelPressed != null ? true : false, + onOkPressed: () { + if (onOkPressed != null) { + onOkPressed(); + } + }, + onCancelPressed: () { + if (onCancelPressed != null) { + context.pop(); + } + }, + ), ); + _isErrorSheetShowing = false; } @override @@ -89,10 +105,10 @@ class DialogServiceImp implements DialogService { Widget exceptionBottomSheetWidget({required BuildContext context, required String message, required Function() onOkPressed, Function()? onCancelPressed}) { return Column( children: [ - (message ?? "").toText16(isBold: false, color: AppColors.textColor), + (message).toText16(isBold: false, color: AppColors.textColor), SizedBox(height: 10.h), SizedBox(height: 24.h), - if (onOkPressed != null && onCancelPressed != null) + if (onCancelPressed != null) Row( children: [ Expanded( @@ -111,7 +127,7 @@ Widget exceptionBottomSheetWidget({required BuildContext context, required Strin SizedBox(width: 5.h), Expanded( child: CustomButton( - text: onCancelPressed != null ? LocaleKeys.confirm.tr() : LocaleKeys.ok.tr(), + text: LocaleKeys.confirm.tr(), onPressed: onOkPressed, backgroundColor: AppColors.bgGreenColor, borderColor: AppColors.bgGreenColor, @@ -121,14 +137,14 @@ Widget exceptionBottomSheetWidget({required BuildContext context, required Strin ), ], ), - if (onOkPressed != null && onCancelPressed == null) + if (onCancelPressed == null) Padding( padding: EdgeInsets.only(bottom: 10.h), child: CustomButton( text: LocaleKeys.ok.tr(), - onPressed: (onOkPressed != null && onCancelPressed == null) + onPressed: (onCancelPressed == null) ? () { - Navigator.of(context).pop(); + context.pop(); } : onOkPressed, backgroundColor: AppColors.primaryRedColor, @@ -143,7 +159,6 @@ Widget exceptionBottomSheetWidget({required BuildContext context, required Strin Widget showPhoneNumberPickerWidget({required BuildContext context, String? message, required Function() onSMSPress, required Function() onWhatsappPress}) { return StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) { - AuthenticationViewModel authViewModel = context.read(); return Column( children: [ (message ?? "").toText16(isBold: false, color: AppColors.textColor), @@ -201,56 +216,3 @@ Widget showPhoneNumberPickerWidget({required BuildContext context, String? messa // ); }); } - -class _ErrorBottomSheet extends StatelessWidget { - final String message; - final Function()? onOkPressed; - - const _ErrorBottomSheet({required this.message, this.onOkPressed}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.error_outline, color: Colors.red, size: 40), - const SizedBox(height: 12), - Text( - "Error", - style: Theme.of(context).textTheme.titleLarge?.copyWith( - color: Colors.red, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 8), - Text( - message, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyMedium, - ), - const SizedBox(height: 16), - ElevatedButton( - onPressed: () { - if (onOkPressed != null) { - onOkPressed!(); - } else { - context.pop(); - } - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - child: const Text("OK", style: TextStyle(color: Colors.white)).onPress(() { - Navigator.of(context).pop(); - }), - ), - ], - ), - ); - } -} diff --git a/lib/services/error_handler_service.dart b/lib/services/error_handler_service.dart index c29dc17..aeefefb 100644 --- a/lib/services/error_handler_service.dart +++ b/lib/services/error_handler_service.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:hmg_patient_app_new/core/exceptions/api_exception.dart'; import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; import 'package:hmg_patient_app_new/core/utils/loading_utils.dart'; -import 'package:hmg_patient_app_new/extensions/route_extensions.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; @@ -28,46 +27,54 @@ class ErrorHandlerServiceImp implements ErrorHandlerService { if (failure is APIException) { loggerService.errorLogs("API Exception: ${failure.message}"); } else if (failure is ServerFailure) { - loggerService.errorLogs("Server Failure: ${failure.message}"); - await _showDialog(failure); + loggerService.errorLogs("URL: ${failure.url} \n Server Failure: ${failure.message}"); + await _showDialog(failure, title: "Server Failure"); } else if (failure is DataParsingFailure) { loggerService.errorLogs("Data Parsing Failure: ${failure.message}"); await _showDialog(failure, title: "Data Error"); } else if (failure is StatusCodeFailure) { loggerService.errorLogs("StatusCode Failure: ${failure.message}"); - await _showDialog(failure, title: "Status Code Failure Error"); + await _showDialog(failure, title: "StatusCodeFailure"); + } else if (failure is ConnectivityFailure) { + loggerService.errorLogs("ConnectivityFailure : ${failure.message}"); + await _showDialog(failure, title: "ConnectivityFailure ", onOkPressed: () {}); + } else if (failure is UnAuthenticatedUserFailure) { + loggerService.errorLogs("URL: ${failure.url} \n UnAuthenticatedUser Failure: ${failure.message}"); + await _showDialog(failure, title: "UnAuthenticatedUser Failure", onOkPressed: () => navigationService.replaceAllRoutesAndNavigateToLanding()); + } else if (failure is AppUpdateFailure) { + loggerService.errorLogs("AppUpdateFailure : ${failure.message}"); + await _showDialog(failure, title: "AppUpdateFailure Error", onOkPressed: () => navigationService.replaceAllRoutesAndNavigateToLanding()); } else if (failure is HttpException) { loggerService.errorLogs("Http Exception: ${failure.message}"); await _showDialog(failure, title: "Network Error"); } else if (failure is UnknownFailure) { - loggerService.errorLogs("Unknown Failure: ${failure.message}"); - await _showDialog(failure, title: "Unknown Error"); + loggerService.errorLogs("URL: ${failure.url} \n Unknown Failure: ${failure.message}"); + await _showDialog(failure, title: "Unknown Failure"); } else if (failure is InvalidCredentials) { loggerService.errorLogs("Invalid Credentials : ${failure.message}"); - await _showDialog(failure, title: "Unknown Error"); + await _showDialog(failure, title: "Invalid Credentials "); } else if (failure is UserIntimationFailure) { if (onUnHandledFailure != null) { onUnHandledFailure(failure); } else { - await _showDialog(failure, title: "Error", onOkPressed: onOkPressed); + await _showDialog(failure, title: "UserIntimationFailure", onOkPressed: onOkPressed); } } else if (failure is MessageStatusFailure) { if (onMessageStatusFailure != null) { onMessageStatusFailure(failure); } else { - await _showDialog(failure, title: "Error", onOkPressed: onOkPressed); + await _showDialog(failure, title: "MessageStatusFailure", onOkPressed: onOkPressed); } } else { loggerService.errorLogs("Unhandled failure type: $failure"); - await _showDialog(failure, title: "Error", onOkPressed: onOkPressed); + await _showDialog(failure, title: "Unhandled Error", onOkPressed: onOkPressed); } } - Future _showDialog(Failure failure, {String title = "Error", Function()? onOkPressed}) async { + Future _showDialog(Failure failure, {required String title, Function()? onOkPressed}) async { if (LoadingUtils.isLoading) { LoadingUtils.hideFullScreenLoader(); } - - await dialogService.showErrorBottomSheet(message: failure.message, onOkPressed: onOkPressed); + await dialogService.showErrorBottomSheet(title: title, message: failure.message, onOkPressed: onOkPressed); } } diff --git a/lib/services/navigation_service.dart b/lib/services/navigation_service.dart index 0c2a901..b5c5e03 100644 --- a/lib/services/navigation_service.dart +++ b/lib/services/navigation_service.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/features/authentication/widgets/otp_verification_screen.dart'; +import 'package:hmg_patient_app_new/routes/app_routes.dart'; class NavigationService { final GlobalKey navigatorKey = GlobalKey(); @@ -18,6 +21,15 @@ class NavigationService { navigatorKey.currentState?.popUntil(ModalRoute.withName(routeName)); } + void replaceAllRoutesAndNavigateToLanding() { + AppState appState = getIt.get(); + appState.isAuthenticated = false; + navigatorKey.currentState?.pushNamedAndRemoveUntil( + AppRoutes.landingScreen, + (Route route) => false, + ); + } + void pushAndReplace(String routeName) { navigatorKey.currentState?.pushReplacementNamed(routeName); } diff --git a/lib/presentation/lab/collapsing_list_view.dart b/lib/widgets/appbar/collapsing_list_view.dart similarity index 100% rename from lib/presentation/lab/collapsing_list_view.dart rename to lib/widgets/appbar/collapsing_list_view.dart diff --git a/lib/widgets/bottom_navigation/bottom_navigation.dart b/lib/widgets/bottom_navigation/bottom_navigation.dart index da289c1..1163c66 100644 --- a/lib/widgets/bottom_navigation/bottom_navigation.dart +++ b/lib/widgets/bottom_navigation/bottom_navigation.dart @@ -12,27 +12,26 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; class BottomNavigation extends StatelessWidget { final int currentIndex; final ValueChanged onTap; + BuildContext? context; - const BottomNavigation({ - super.key, - required this.currentIndex, - required this.onTap, - }); + BottomNavigation({super.key, required this.currentIndex, required this.onTap, this.context}); @override Widget build(BuildContext context) { - AppState appState = getIt.get(); + AppState appState = getIt.get(); final items = [ - BottomNavItem(icon: AppAssets.homeBottom, label: LocaleKeys.home.tr()), - appState.isAuthenticated ? BottomNavItem(icon: AppAssets.myFilesBottom, label: LocaleKeys.myFiles.tr()) : BottomNavItem(icon: AppAssets.feedback, label: LocaleKeys.feedback.tr()), + BottomNavItem(icon: AppAssets.homeBottom, label: LocaleKeys.home.tr(context: context)), + appState.isAuthenticated + ? BottomNavItem(icon: AppAssets.myFilesBottom, label: LocaleKeys.myFiles.tr(context: context)) + : BottomNavItem(icon: AppAssets.feedback, label: LocaleKeys.feedback.tr()), BottomNavItem( icon: AppAssets.bookAppoBottom, - label: LocaleKeys.appointment.tr(), + label: LocaleKeys.appointment.tr(context: context), iconSize: 27, isSpecial: true, ), - appState.isAuthenticated ? BottomNavItem(icon: AppAssets.toDoBottom, label: LocaleKeys.todoList.tr()) : BottomNavItem(icon: AppAssets.news, label: LocaleKeys.news.tr()) , - BottomNavItem(icon: AppAssets.servicesBottom, label: LocaleKeys.services2.tr()), + appState.isAuthenticated ? BottomNavItem(icon: AppAssets.toDoBottom, label: LocaleKeys.todoList.tr(context: context)) : BottomNavItem(icon: AppAssets.news, label: LocaleKeys.news.tr()), + BottomNavItem(icon: AppAssets.servicesBottom, label: LocaleKeys.services2.tr(context: context)), ]; return Container( @@ -42,7 +41,7 @@ class BottomNavigation extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceAround, children: List.generate( items.length, - (index) => _buildNavItem(items[index], index), + (index) => _buildNavItem(items[index], index), ), ), ); @@ -57,23 +56,22 @@ class BottomNavigation extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - Center( - child: Utils.buildSvgWithAssets( - icon: item.icon, - height: item.iconSize.h, - width: item.iconSize.h, - // iconColor: isSelected ? Colors.black87 : Colors.black87, - ), + Center( + child: Utils.buildSvgWithAssets( + icon: item.icon, + height: item.iconSize.h, + width: item.iconSize.h, + // iconColor: isSelected ? Colors.black87 : Colors.black87, ), + ), const SizedBox(height: 10), item.label.toText12( - fontWeight:FontWeight.w500, + fontWeight: FontWeight.w500, // color: Colors.black87, // textAlign: TextAlign.center, ), - SizedBox(height: item.isSpecial ? 5:0 ) + SizedBox(height: item.isSpecial ? 5 : 0) ], - ), ); } diff --git a/lib/widgets/bottomsheet/exception_bottom_sheet.dart b/lib/widgets/bottomsheet/exception_bottom_sheet.dart index f0ea651..c11dec1 100644 --- a/lib/widgets/bottomsheet/exception_bottom_sheet.dart +++ b/lib/widgets/bottomsheet/exception_bottom_sheet.dart @@ -11,13 +11,14 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; class ExceptionBottomSheet extends StatelessWidget { - String message; - bool showOKButton; - bool showCancel; - Function() onOkPressed; - Function()? onCancelPressed; + final String? title; + final String message; + final bool showOKButton; + final bool showCancel; + final Function() onOkPressed; + final Function()? onCancelPressed; - ExceptionBottomSheet({Key? key, required this.message, this.showOKButton = true, this.showCancel = false, required this.onOkPressed, this.onCancelPressed}) : super(key: key); + const ExceptionBottomSheet({super.key, this.title, required this.message, this.showOKButton = true, this.showCancel = false, required this.onOkPressed, this.onCancelPressed}); @override Widget build(BuildContext context) { @@ -44,7 +45,7 @@ class ExceptionBottomSheet extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.notice.tr().toText28(), + (title ?? LocaleKeys.notice.tr()).toText28(), InkWell( onTap: () { Navigator.of(context).pop(); @@ -54,7 +55,7 @@ class ExceptionBottomSheet extends StatelessWidget { ], ), SizedBox(height: 10.h), - (message ?? "").toText16(isBold: false, color: AppColors.textColor), + (message).toText16(isBold: false, color: AppColors.textColor), SizedBox(height: 10.h), SizedBox(height: 24.h), if (showOKButton && showCancel) diff --git a/lib/widgets/buttons/custom_button.dart b/lib/widgets/buttons/custom_button.dart index 5331e43..2f8a9ec 100644 --- a/lib/widgets/buttons/custom_button.dart +++ b/lib/widgets/buttons/custom_button.dart @@ -71,7 +71,7 @@ class CustomButton extends StatelessWidget { child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconSize, height: iconSize), ), Padding( - padding: EdgeInsets.only(top: 2.5), + padding: EdgeInsets.only(top: 0), child: Text( text, overflow: textOverflow, diff --git a/lib/widgets/graph/custom_graph.dart b/lib/widgets/graph/custom_graph.dart index ee246ad..fa72b19 100644 --- a/lib/widgets/graph/custom_graph.dart +++ b/lib/widgets/graph/custom_graph.dart @@ -3,40 +3,43 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:hmg_patient_app_new/core/common_models/data_points.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; +/// A customizable line graph widget using `fl_chart`. /// -/// CustomGraph(dataPoints: sampleData, scrollDirection: Axis.horizontal,height: 200,maxY: 100, maxX:2.5, -/// leftLabelFormatter: (value){ -/// Widget buildLabel(String label) { -/// return Padding( -/// padding: const EdgeInsets.only(right: 8), -/// child: Text( -/// label, -/// style: TextStyle( -/// fontSize: 8.fSize, color: AppColors.textColor, -/// fontFamily: -/// FontUtils.getFontFamilyForLanguage(false) -/// ), -/// textAlign: TextAlign.right, -/// ), -/// ); -/// } -/// switch (value.toInt()) { +/// Displays a line chart with configurable axis labels, colors, and data points. +/// Useful for visualizing time series or other sequential data. /// -/// case 20: -/// return buildLabel("Critical Low"); -/// case 40: -/// return buildLabel("Low"); -/// case 60: -/// return buildLabel("Normal"); -/// case 80: -/// return buildLabel("High"); -/// case 100: -/// return buildLabel("Critical High"); -/// } -/// return const SizedBox.shrink(); -/// }, +/// **Parameters:** +/// - [dataPoints]: List of `DataPoint` objects to plot. +/// - [leftLabelFormatter]: Function to build left axis labels. +/// - [bottomLabelFormatter]: Function to build bottom axis labels. +/// - [width]: Optional width of the chart. +/// - [height]: Required height of the chart. +/// - [maxY], [maxX], [minX]: Axis bounds. +/// - [spotColor]: Color of the touched spot marker. +/// - [graphColor]: Color of the line. +/// - [graphShadowColor]: Color of the area below the line. +/// - [graphGridColor]: Color of the grid lines. +/// - [bottomLabelColor]: Color of bottom axis labels. +/// - [bottomLabelSize]: Font size for bottom axis labels. +/// - [bottomLabelFontWeight]: Font weight for bottom axis labels. +/// - [leftLabelInterval]: Interval between left axis labels. +/// - [leftLabelReservedSize]: Reserved space for left axis labels. +/// - [scrollDirection]: Axis direction for scrolling. +/// - [showBottomTitleDates]: Whether to show bottom axis labels. +/// - [isFullScreeGraph]: Whether the graph is fullscreen. +/// - [makeGraphBasedOnActualValue]: Use `actualValue` for plotting. /// -/// ), +/// Example usage: +/// ```dart +/// CustomGraph( +/// dataPoints: sampleData, +/// leftLabelFormatter: (value) => ..., +/// bottomLabelFormatter: (value, dataPoints) => ..., +/// height: 200, +/// scrollDirection: Axis.horizontal, +/// maxY: 100, +/// maxX: 2.5, +/// ) class CustomGraph extends StatelessWidget { final List dataPoints; final double? width; @@ -51,6 +54,8 @@ class CustomGraph extends StatelessWidget { final Color bottomLabelColor; final double? bottomLabelSize; final FontWeight? bottomLabelFontWeight; + final double? leftLabelInterval; + final double? leftLabelReservedSize; ///creates the left label and provide it to the chart as it will be used by other part of the application so the label will be different for every chart final Widget Function(double) leftLabelFormatter; @@ -60,6 +65,7 @@ class CustomGraph extends StatelessWidget { final Axis scrollDirection; final bool showBottomTitleDates; final bool isFullScreeGraph; + final bool makeGraphBasedOnActualValue; const CustomGraph({ super.key, @@ -79,6 +85,9 @@ class CustomGraph extends StatelessWidget { this.bottomLabelColor = AppColors.textColor, this.bottomLabelFontWeight = FontWeight.w500, this.bottomLabelSize, + this.leftLabelInterval, + this.leftLabelReservedSize, + this.makeGraphBasedOnActualValue = false, required this.bottomLabelFormatter, this.minX, }); @@ -87,13 +96,7 @@ class CustomGraph extends StatelessWidget { Widget build(BuildContext context) { // var maxY = 0.0; double interval = 20; - if ((maxY ?? 0) > 10 && (maxY ?? 0) <= 20) { - interval = 2; - } else if ((maxY ?? 0) > 5 && (maxY ?? 0) <= 10) { - interval = 1; - } else if ((maxY ?? 0) >= 0 && (maxY ?? 0) <= 5) { - interval = .4; - } + return Material( color: Colors.white, child: SizedBox( @@ -102,11 +105,11 @@ class CustomGraph extends StatelessWidget { child: LineChart( LineChartData( minY: 0, - maxY: - ((maxY?.ceilToDouble() ?? 0.0) + interval).floorToDouble(), + // maxY: ((maxY?.ceilToDouble() ?? 0.0) + interval).floorToDouble(), + maxY: maxY, // minX: dataPoints.first.labelValue - 1, maxX: maxX, - minX: minX ??-0.2, + minX: minX , lineTouchData: LineTouchData( getTouchLineEnd: (_, __) => 0, getTouchedSpotIndicator: (barData, indicators) { @@ -140,7 +143,7 @@ class CustomGraph extends StatelessWidget { return LineTooltipItem( // '${dataPoint.label} ${spot.y.toStringAsFixed(2)}', - '${dataPoint.actualValue} ${dataPoint.displayTime}', + '${dataPoint.value} - ${dataPoint.actualValue} - ${dataPoint.displayTime}', TextStyle( color: Colors.black, fontSize: 12.fSize, @@ -156,8 +159,8 @@ class CustomGraph extends StatelessWidget { leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, - reservedSize: 77, - interval: .1, // Let fl_chart handle it + reservedSize: leftLabelReservedSize??80, + interval: leftLabelInterval ?? .1, // Let fl_chart handle it getTitlesWidget: (value, _) { return leftLabelFormatter(value); }, @@ -190,12 +193,12 @@ class CustomGraph extends StatelessWidget { gridData: FlGridData( show: true, drawVerticalLine: false, - horizontalInterval: 20, + // horizontalInterval: 40, checkToShowHorizontalLine: (value) => value >= 0 && value <= 100, getDrawingHorizontalLine: (value) { return FlLine( - color: AppColors.graphGridColor, + color: graphGridColor, strokeWidth: 1, dashArray: [5, 5], ); @@ -208,7 +211,9 @@ class CustomGraph extends StatelessWidget { List _buildColoredLineSegments(List dataPoints) { final List allSpots = dataPoints.asMap().entries.map((entry) { - return FlSpot(entry.key.toDouble(), entry.value.value); + double value = (makeGraphBasedOnActualValue)?double.tryParse(entry.value.actualValue)??0.0:entry.value.value; + debugPrint("the value is $value"); + return FlSpot(entry.key.toDouble(), value); }).toList(); var data = [ diff --git a/pubspec.yaml b/pubspec.yaml index adf737b..3bdaa4a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,7 +55,7 @@ dependencies: uuid: ^4.5.1 health: ^13.1.3 # health: 12.0.1 - fl_chart: ^1.1.1 + fl_chart: ^1.0.0 geolocator: ^14.0.2 dropdown_search: ^6.0.2 google_maps_flutter: ^2.12.3