LiveCare shcedule implementation contd.

pull/62/head
haroon amjad 1 month ago
parent 938d95cb06
commit a711d3a6dd

@ -176,8 +176,8 @@ class ApiClientImp implements ApiClient {
body[_appState.isAuthenticated ? 'TokenID' : 'LogInTokenID'] = _appState.appAuthToken;
}
// body['TokenID'] = "@dm!n";
// body['PatientID'] = 3628599;
body['TokenID'] = "@dm!n";
body['PatientID'] = 4767477;
}
body.removeWhere((key, value) => value == null);

@ -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

@ -365,7 +365,7 @@ extension DynamicTextStyleExtension on BuildContext {
TextBaseline? textBaseline,
FontStyle? fontStyle,
bool isLanguageSwitcher = false}) {
final family = FontUtils.getFontFamilyForLanguage(true);
final family = FontUtils.getFontFamilyForLanguage(false); // TODO: @Aamir make it dynamic based on app language
return TextStyle(
fontFamily: family,
fontSize: fontSize,

@ -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,13 @@ abstract class BookAppointmentsRepo {
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getProjectList();
Future<Either<Failure, GenericApiModel<List<GetClinicsListResponseModel>>>> getClinicsWithRespectToClinicId(String projectID);
Future<Either<Failure, GenericApiModel<List<GetLiveCareClinicsResponseModel>>>> getLiveCareScheduleClinics(int age, int genderID);
Future<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getLiveCareDoctorsList(int serviceID, int age, int genderID, {Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> getLiveCareDoctorFreeSlots(int clinicID, int serviceID, int projectID, int doctorId, bool isBookingForLiveCare,
{Function(dynamic)? onSuccess, Function(String)? onError});
}
class BookAppointmentsRepoImp implements BookAppointmentsRepo {
@ -443,4 +451,133 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<GetLiveCareClinicsResponseModel>>>> getLiveCareScheduleClinics(int age, int genderID) async {
Map<String, dynamic> mapDevice = {"Age": age, "Gender": genderID};
try {
GenericApiModel<List<GetLiveCareClinicsResponseModel>>? 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<String, dynamic>)).toList().cast<GetLiveCareClinicsResponseModel>();
apiResponse = GenericApiModel<List<GetLiveCareClinicsResponseModel>>(
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<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getLiveCareDoctorsList(int serviceID, int age, int genderID,
{Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
"ServiceID": serviceID,
"Age": age,
"Gender": genderID,
};
try {
GenericApiModel<List<DoctorsListResponseModel>>? 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<String, dynamic>)).toList().cast<DoctorsListResponseModel>();
apiResponse = GenericApiModel<List<DoctorsListResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: doctorsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> getLiveCareDoctorFreeSlots(int clinicID, int serviceID, int projectID, int doctorId, bool isBookingForLiveCare,
{Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
"DoctorID": doctorId,
"IsBookingForLiveCare": true,
"ClinicID": clinicID,
"ServiceID": serviceID,
"ProjectID": projectID,
"OriginalClinicID": clinicID,
"days": 50,
"isReschadual": false,
};
try {
GenericApiModel<dynamic>? 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<dynamic>(
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()));
}
}
}

@ -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<GetClinicsListResponseModel> clinicsList = [];
List<GetClinicsListResponseModel> _filteredClinicsList = [];
List<GetLiveCareClinicsResponseModel> liveCareClinicsList = [];
List<GetClinicsListResponseModel> get filteredClinicsList => _filteredClinicsList;
List<DoctorsListResponseModel> doctorsList = [];
List<DoctorsListResponseModel> 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,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
isClinicsListLoading = true;
isDoctorsListLoading = true;
isDoctorProfileLoading = true;
isLiveCareSchedule = false;
clinicsList.clear();
doctorsList.clear();
liveCareClinicsList.clear();
// getLocation();
notifyListeners();
}
@ -154,6 +167,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 +184,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<void> getClinics() async
{
Future<void> getClinics() async {
if (shouldLoadSpecificClinic) {
getRegionSelectedClinics();
} else if (isLiveCareSchedule) {
getLiveCareScheduleClinics();
} else {
getAllClinics();
}
@ -191,6 +215,53 @@ class BookAppointmentsViewModel extends ChangeNotifier {
);
}
Future<void> getLiveCareScheduleClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
liveCareClinicsList.clear();
final result = await bookAppointmentsRepo.getLiveCareScheduleClinics(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!);
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<void> getLiveCareDoctorsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear();
final result =
await bookAppointmentsRepo.getLiveCareDoctorsList(selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, onError: onError);
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<void> getDoctorsList(
{int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
@ -238,7 +309,6 @@ class BookAppointmentsViewModel extends ChangeNotifier {
);
}
//TODO: Handle the cases for LiveCare Schedule
Future<void> getDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
docFreeSlots.clear();
DateTime date;
@ -281,6 +351,50 @@ class BookAppointmentsViewModel extends ChangeNotifier {
);
}
Future<void> 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<DateTime, List> _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<void> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel);
@ -419,22 +533,21 @@ class BookAppointmentsViewModel extends ChangeNotifier {
final result = await bookAppointmentsRepo.getProjectList();
result.fold(
(failure) async =>
await errorHandlerService.handleError(failure: failure),
(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;
@ -455,15 +568,14 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} else {
filteredHospitalList = RegionList();
var list = isHMG
? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList
: hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList;
var list = isHMG ? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList : hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList;
if(list != null && list.isEmpty){ notifyListeners(); 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) {
regionData.hmgDoctorList = filteredList;
@ -473,9 +585,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
regionData.hmcSize = filteredList.length;
}
filteredHospitalList?.registeredDoctorMap = {
selectedRegionId! : regionData
};
filteredHospitalList?.registeredDoctorMap = {selectedRegionId!: regionData};
}
notifyListeners();
}
@ -515,11 +625,11 @@ class BookAppointmentsViewModel extends ChangeNotifier {
},
);
}
void resetFilterList() {
filteredHospitalList = hospitalList;
}
void getLocation() {
locationUtils.getLocation();
}

@ -12,8 +12,8 @@ class GetClinicsListResponseModel {
GetClinicsListResponseModel.fromJson(Map<String, dynamic> 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'];

@ -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<String, dynamic> 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<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
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;
}
}

@ -42,6 +42,7 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
@override
void initState() {
scheduleMicrotask(() {
bookAppointmentsViewModel.selectedTabIndex = 0;
bookAppointmentsViewModel.initBookAppointmentViewModel();
bookAppointmentsViewModel.getLocation();
});
@ -123,6 +124,7 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
).onPress(() {
bookAppointmentsViewModel.setIsClinicsListLoading(true);
bookAppointmentsViewModel.setLoadSpecificClinic(false);
bookAppointmentsViewModel.setIsLiveCareSchedule(false);
bookAppointmentsViewModel.setProjectID(null);
Navigator.of(context).push(
CustomPageRoute(
@ -191,6 +193,112 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
),
],
).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);
}),
],
),
),
),
],
).paddingSymmetrical(24.h, 0.h);
default:
SizedBox.shrink();
}
@ -200,11 +308,7 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
void openRegionListBottomSheet(BuildContext context) {
regionalViewModel.flush();
// AppointmentViaRegionViewmodel? viewmodel = null;
showCommonBottomSheetWithoutHeight(context,
title: "",
titleWidget: Consumer<AppointmentViaRegionViewmodel>(
builder: (_, data, __) => getTitle(data)),
isDismissible: false,
showCommonBottomSheetWithoutHeight(context, title: "", titleWidget: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) => getTitle(data)), isDismissible: false,
child: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) {
return getRegionalSelectionWidget(data);
}), callBackFunc: () {});
@ -238,19 +342,16 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
if (data.selectedRegionId == null) {
return LocaleKeys.selectRegion.tr().toText20(weight: FontWeight.w600);
} else {
return
Transform.flip(
return Transform.flip(
flipX: data.isArabic ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.arrow_back,
iconColor: Color(0xff2B353E),
fit: BoxFit.contain,
),
).onPress(() {
data.handleBackPress();
});
}
}
}

@ -12,6 +12,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/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/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/book_appointment/select_livecare_clinic_page.dart';
@ -58,7 +59,7 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
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,7 +95,46 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
horizontal: ResponsiveExtension(15).h,
),
),
ListView.separated(
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(),
@ -102,6 +142,8 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
itemBuilder: (context, index) {
return bookAppointmentsVM.isClinicsListLoading
? ClinicCard(
bookAppointmentsVM: bookAppointmentsVM,
liveCareClinicsResponseModel: GetLiveCareClinicsResponseModel(),
clinicsListResponseModel: GetClinicsListResponseModel(),
isLoading: bookAppointmentsVM.isClinicsListLoading,
)
@ -116,6 +158,8 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
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(() {
@ -137,6 +181,16 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
);
}
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);

@ -42,7 +42,11 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
@override
void initState() {
scheduleMicrotask(() {
if (bookAppointmentsViewModel.isLiveCareSchedule) {
bookAppointmentsViewModel.getLiveCareDoctorsList();
} else {
bookAppointmentsViewModel.getDoctorsList();
}
});
super.initState();
}
@ -96,7 +100,8 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
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<SelectDoctorPage> {
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) {

@ -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) {
@ -35,7 +39,10 @@ class ClinicCard extends StatelessWidget {
]),
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)),

@ -108,7 +108,31 @@ class DoctorCard extends StatelessWidget {
onPressed: () async {
bookAppointmentsViewModel.setSelectedDoctor(doctorsListResponseModel);
LoaderBottomSheet.showLoader();
await bookAppointmentsViewModel.getDoctorFreeSlots(
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();

@ -34,6 +34,7 @@ class _LandingNavigationState extends State<LandingNavigation> {
],
),
bottomNavigationBar: BottomNavigation(
context: context,
currentIndex: _currentIndex,
onTap: (index) {
setState(() => _currentIndex = index);

@ -12,27 +12,26 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
class BottomNavigation extends StatelessWidget {
final int currentIndex;
final ValueChanged<int> 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>();
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(
@ -73,7 +72,6 @@ class BottomNavigation extends StatelessWidget {
),
SizedBox(height: item.isSpecial ? 5 : 0)
],
),
);
}

@ -66,7 +66,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,
style: context.dynamicTextStyle(

Loading…
Cancel
Save