doctor search filter design update & switch user bug fix.

dev_aamir
aamir-csol 3 days ago
parent f62c7c9b20
commit 6725ab028d

@ -246,7 +246,6 @@ class AuthenticationViewModel extends ChangeNotifier {
}
Future<void> _handleNewImeiRegistration() async {
await selectDeviceImei(onSuccess: (dynamic respData) async {
try {
if (respData != null) {
@ -461,6 +460,12 @@ class AuthenticationViewModel extends ChangeNotifier {
bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true || _appState.getUserRegistrationPayload.patientOutSa == 1);
MyAppointmentsViewModel myAppointmentsVM = getIt<MyAppointmentsViewModel>();
if (isSwitchUser && _appState.getSuperUserID == null) {
nationalIdController.text = responseID.toString();
}else if( isSwitchUser && _appState.getSuperUserID != null){
nationalIdController.text = _appState.getSuperUserID.toString();
}
final request = RequestUtils.getCommonRequestWelcome(
phoneNumber: phoneNumberController.text,
otpTypeEnum: otpTypeEnum,
@ -761,14 +766,14 @@ class AuthenticationViewModel extends ChangeNotifier {
phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0")
? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "")
: _appState.getAuthenticatedUser()!.mobileNumber)!;
nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!;
nationalIdController.text = _appState.getAuthenticatedUser()!.patientIdentificationNo!;
onSuccess();
} else if ((loginTypeEnum == LoginTypeEnum.sms || loginTypeEnum == LoginTypeEnum.whatsapp && _appState.getSelectDeviceByImeiRespModelElement == null) &&
_appState.getAuthenticatedUser() != null) {
phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0")
? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "")
: _appState.getAuthenticatedUser()!.mobileNumber)!;
nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!;
nationalIdController.text = _appState.getAuthenticatedUser()!.patientIdentificationNo!;
onSuccess();
}
}

@ -53,6 +53,9 @@ class BookAppointmentsViewModel extends ChangeNotifier {
int? calculationID = 0;
bool isSortByClinic = true;
// Accordion expansion state
int? expandedGroupIndex;
int initialSlotDuration = 0;
bool isNearestAppointmentSelected = false;
@ -158,13 +161,28 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool isBodyPartsLoading = false;
int duration = 0;
setIsSortByClinic(bool value) {
isSortByClinic = value;
doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital;
notifyListeners();
}
// Toggle accordion expansion
void toggleGroupExpansion(int index) {
if (expandedGroupIndex == index) {
expandedGroupIndex = null;
} else {
expandedGroupIndex = index;
}
notifyListeners();
}
// Reset accordion expansion
void resetGroupExpansion() {
expandedGroupIndex = null;
notifyListeners();
}
// Sort filtered doctor list by clinic or hospital
void sortFilteredDoctorList(bool sortByClinic) {
isSortByClinic = sortByClinic;
@ -186,6 +204,22 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
// Group filtered doctors list for accordion display
List<List<DoctorsListResponseModel>> getGroupedFilteredDoctorsList() {
final clinicMap = <String, List<DoctorsListResponseModel>>{};
final hospitalMap = <String, List<DoctorsListResponseModel>>{};
for (var doctor in filteredDoctorList) {
final clinicKey = (doctor.clinicName ?? 'Unknown').trim();
clinicMap.putIfAbsent(clinicKey, () => []).add(doctor);
final hospitalKey = (doctor.projectName ?? 'Unknown').trim();
hospitalMap.putIfAbsent(hospitalKey, () => []).add(doctor);
}
return isSortByClinic ? clinicMap.values.toList() : hospitalMap.values.toList();
}
// Group doctors by clinic and hospital
void _groupDoctorsList() {
final clinicMap = <String, List<DoctorsListResponseModel>>{};
@ -237,6 +271,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
isDoctorProfileLoading = true;
isLiveCareSchedule = false;
currentlySelectedHospitalFromRegionFlow = null;
expandedGroupIndex = null; // Reset accordion state
clinicsList.clear();
doctorsList.clear();
liveCareClinicsList.clear();

@ -726,6 +726,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
}
Future<void> getPatientAppointmentQueueDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async {
//TODO: Discuss With Haroon, Is the User Has no data it return No Element Bad State;
isAppointmentQueueDetailsLoading = true;
notifyListeners();
final result = await myAppointmentsRepo.getPatientAppointmentQueueDetails(

@ -34,6 +34,14 @@ class _SearchDoctorByNameState extends State<SearchDoctorByName> {
TextEditingController searchEditingController = TextEditingController();
FocusNode textFocusNode = FocusNode();
late BookAppointmentsViewModel bookAppointmentsViewModel;
late ScrollController _scrollController;
final Map<int, GlobalKey> _itemKeys = {};
@override
void initState() {
_scrollController = ScrollController();
super.initState();
}
@override
Widget build(BuildContext context) {
@ -173,8 +181,11 @@ class _SearchDoctorByNameState extends State<SearchDoctorByName> {
padding: EdgeInsets.only(top: 20.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : bookAppointmentsVM.filteredDoctorList.length,
itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : bookAppointmentsVM.getGroupedFilteredDoctorsList().length,
itemBuilder: (context, index) {
final isExpanded = bookAppointmentsVM.expandedGroupIndex == index;
final groupedDoctors = bookAppointmentsVM.getGroupedFilteredDoctorsList();
return bookAppointmentsVM.isDoctorsListLoading
? DoctorCard(
doctorsListResponseModel: DoctorsListResponseModel(),
@ -188,35 +199,113 @@ class _SearchDoctorByNameState extends State<SearchDoctorByName> {
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
key: _itemKeys.putIfAbsent(index, () => GlobalKey()),
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: DoctorCard(
doctorsListResponseModel: bookAppointmentsVM.filteredDoctorList[index],
isLoading: false,
bookAppointmentsViewModel: bookAppointmentsViewModel,
).onPress(() async {
bookAppointmentsVM.setSelectedDoctor(bookAppointmentsVM.filteredDoctorList[index]);
// bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel());
LoaderBottomSheet.showLoader();
await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorProfilePage(),
),
);
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
}),
child: InkWell(
onTap: () {
bookAppointmentsVM.toggleGroupExpansion(index);
// After rebuild, ensure the expanded item is visible
WidgetsBinding.instance.addPostFrameCallback((_) {
final key = _itemKeys[index];
if (key != null && key.currentContext != null && bookAppointmentsVM.expandedGroupIndex == index) {
Scrollable.ensureVisible(
key.currentContext!,
duration: Duration(milliseconds: 350),
curve: Curves.easeInOut,
alignment: 0.1,
);
}
});
},
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomButton(
text: "${groupedDoctors[index].length} ${'doctors'.needTranslation}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h),
Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
],
),
SizedBox(height: 8.h),
// Clinic/Hospital name as group title
Text(
bookAppointmentsVM.isSortByClinic
? (groupedDoctors[index].first.clinicName ?? 'Unknown')
: (groupedDoctors[index].first.projectName ?? 'Unknown'),
style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600),
overflow: TextOverflow.ellipsis,
),
// Expanded content - list of doctors in this group
AnimatedSwitcher(
duration: Duration(milliseconds: 400),
child: isExpanded
? Container(
key: ValueKey<int>(index),
padding: EdgeInsets.only(top: 12.h),
child: Column(
children: groupedDoctors[index].asMap().entries.map<Widget>((entry) {
final doctorIndex = entry.key;
final doctor = entry.value;
final isLastDoctor = doctorIndex == groupedDoctors[index].length - 1;
return Column(
children: [
DoctorCard(
doctorsListResponseModel: doctor,
isLoading: false,
bookAppointmentsViewModel: bookAppointmentsViewModel,
).onPress(() async {
bookAppointmentsVM.setSelectedDoctor(doctor);
LoaderBottomSheet.showLoader();
await bookAppointmentsVM.getDoctorProfile(
onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorProfilePage(),
),
);
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
}),
if (!isLastDoctor)
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h),
],
);
}).toList(),
),
)
: SizedBox.shrink(),
),
],
),
),
),
),
),
),
@ -296,6 +385,7 @@ class _SearchDoctorByNameState extends State<SearchDoctorByName> {
@override
void dispose() {
_scrollController.dispose();
bookAppointmentsViewModel.doctorsList.clear();
super.dispose();
}

@ -33,7 +33,6 @@ class SelectDoctorPage extends StatefulWidget {
class _SelectDoctorPageState extends State<SelectDoctorPage> {
TextEditingController searchEditingController = TextEditingController();
int? expandedIndex;
FocusNode textFocusNode = FocusNode();
@ -170,8 +169,7 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
),
],
).paddingSymmetrical(0.h, 0.h),
if (bookAppointmentsViewModel.isGetDocForHealthCal && bookAppointmentsVM.showSortFilterButtons)
SizedBox(height: 16.h),
if (bookAppointmentsViewModel.isGetDocForHealthCal && bookAppointmentsVM.showSortFilterButtons) SizedBox(height: 16.h),
Row(
mainAxisSize: MainAxisSize.max,
children: [
@ -190,7 +188,6 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
value: bookAppointmentsVM.isNearestAppointmentSelected,
onChanged: (newValue) async {
bookAppointmentsVM.setIsNearestAppointmentSelected(newValue);
},
),
],
@ -199,11 +196,9 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
padding: EdgeInsets.only(top: 16.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: bookAppointmentsVM.isDoctorsListLoading
? 5
: (bookAppointmentsVM.doctorsListGrouped.isNotEmpty ? bookAppointmentsVM.doctorsListGrouped.length : 1),
itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : (bookAppointmentsVM.doctorsListGrouped.isNotEmpty ? bookAppointmentsVM.doctorsListGrouped.length : 1),
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
final isExpanded = bookAppointmentsVM.expandedGroupIndex == index;
return bookAppointmentsVM.isDoctorsListLoading
? DoctorCard(
doctorsListResponseModel: DoctorsListResponseModel(),
@ -225,13 +220,11 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: InkWell(
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
bookAppointmentsVM.toggleGroupExpansion(index);
// After rebuild, ensure the expanded item is visible
WidgetsBinding.instance.addPostFrameCallback((_) {
final key = _itemKeys[index];
if (key != null && key.currentContext != null && expandedIndex == index) {
if (key != null && key.currentContext != null && bookAppointmentsVM.expandedGroupIndex == index) {
Scrollable.ensureVisible(
key.currentContext!,
duration: Duration(milliseconds: 350),
@ -282,34 +275,41 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
key: ValueKey<int>(index),
padding: EdgeInsets.only(top: 12.h),
child: Column(
children: bookAppointmentsVM.doctorsListGrouped[index].map<Widget>((doctor) {
return Container(
margin: EdgeInsets.only(bottom: 12.h),
child: DoctorCard(
doctorsListResponseModel: doctor,
isLoading: false,
bookAppointmentsViewModel: bookAppointmentsViewModel,
).onPress(() async {
bookAppointmentsVM.setSelectedDoctor(doctor);
LoaderBottomSheet.showLoader();
await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorProfilePage(),
),
);
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
}),
children: bookAppointmentsVM.doctorsListGrouped[index].asMap().entries.map<Widget>((entry) {
final doctorIndex = entry.key;
final doctor = entry.value;
final isLastDoctor = doctorIndex == bookAppointmentsVM.doctorsListGrouped[index].length - 1;
return Column(
children: [
DoctorCard(
doctorsListResponseModel: doctor,
isLoading: false,
bookAppointmentsViewModel: bookAppointmentsViewModel,
).onPress(() async {
bookAppointmentsVM.setSelectedDoctor(doctor);
LoaderBottomSheet.showLoader();
await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorProfilePage(),
),
);
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
}),
if (!isLastDoctor)
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h),
],
);
}).toList(),
),

@ -38,7 +38,7 @@ class DoctorCard extends StatelessWidget {
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(14.h),
padding: EdgeInsets.only(top: 14.h,bottom: 20.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [

Loading…
Cancel
Save