From e72e67d9bf6931227f23c2a16ff155767b3079a3 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Tue, 9 Dec 2025 16:23:47 +0300 Subject: [PATCH] Appointment Queue implemented, Blood Donation implementation contd. --- .../main/res/values/mapbox_access_token.xml | 3 + assets/images/svg/select_city_icon.svg | 4 + lib/core/app_assets.dart | 1 + lib/core/dependencies.dart | 7 + lib/core/utils/utils.dart | 50 +++ .../blood_donation/blood_donation_repo.dart | 95 ++++ .../blood_donation_view_model.dart | 133 ++++++ .../models/blood_group_list_model.dart | 6 + .../models/blood_group_response_model.dart | 61 +++ .../blood_donation/models/cities_model.dart | 21 + .../book_appointments_view_model.dart | 4 +- .../my_appointments_view_model.dart | 9 + lib/main.dart | 4 + .../appointments/appointment_queue_page.dart | 404 ++++++++---------- .../widgets/appointment_card.dart | 83 ++-- .../appointment_checkin_bottom_sheet.dart | 5 +- .../widgets/appointment_doctor_card.dart | 37 +- .../widgets/appointment_queueing_screen.dart | 0 .../blood_donation/blood_donation_page.dart | 172 ++++++++ .../widgets/city_list_item.dart | 60 +++ .../widgets/select_city_widget.dart | 39 ++ .../widgets/hospital_list_item.dart | 36 -- .../hmg_services/services_page.dart | 10 + lib/presentation/home/landing_page.dart | 85 +++- lib/routes/app_routes.dart | 5 +- 25 files changed, 1015 insertions(+), 319 deletions(-) create mode 100644 android/app/src/main/res/values/mapbox_access_token.xml create mode 100644 assets/images/svg/select_city_icon.svg create mode 100644 lib/features/blood_donation/blood_donation_repo.dart create mode 100644 lib/features/blood_donation/blood_donation_view_model.dart create mode 100644 lib/features/blood_donation/models/blood_group_list_model.dart create mode 100644 lib/features/blood_donation/models/blood_group_response_model.dart create mode 100644 lib/features/blood_donation/models/cities_model.dart delete mode 100644 lib/presentation/appointments/widgets/appointment_queueing_screen.dart create mode 100644 lib/presentation/blood_donation/blood_donation_page.dart create mode 100644 lib/presentation/blood_donation/widgets/city_list_item.dart create mode 100644 lib/presentation/blood_donation/widgets/select_city_widget.dart diff --git a/android/app/src/main/res/values/mapbox_access_token.xml b/android/app/src/main/res/values/mapbox_access_token.xml new file mode 100644 index 0000000..f1daf69 --- /dev/null +++ b/android/app/src/main/res/values/mapbox_access_token.xml @@ -0,0 +1,3 @@ + + sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg + \ No newline at end of file diff --git a/assets/images/svg/select_city_icon.svg b/assets/images/svg/select_city_icon.svg new file mode 100644 index 0000000..ff66079 --- /dev/null +++ b/assets/images/svg/select_city_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 5bc984a..95f24ca 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -181,6 +181,7 @@ class AppAssets { static const String call_for_vitals = '$svgBasePath/call_for_vitals.svg'; static const String call_for_doctor = '$svgBasePath/call_for_doctor.svg'; static const String bulb_icon = '$svgBasePath/bulb_icon.svg'; + static const String select_city_icon = '$svgBasePath/select_city_icon.svg'; //bottom navigation// diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index 30b4ab7..c7037e5 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -5,6 +5,8 @@ import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/location_util.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_repo.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/common/common_repo.dart'; @@ -116,6 +118,7 @@ class AppDependencies { getIt.registerLazySingleton(() => LocationRepoImpl(apiClient: getIt())); getIt.registerLazySingleton(() => ContactUsRepoImp(loggerService: getIt(), apiClient: getIt())); getIt.registerLazySingleton(() => HmgServicesRepoImp(loggerService: getIt(), apiClient: getIt())); + getIt.registerLazySingleton(() => BloodDonationRepoImp(loggerService: getIt(), apiClient: getIt())); // ViewModels // Global/shared VMs → LazySingleton @@ -224,6 +227,10 @@ class AppDependencies { () => HmgServicesViewModel(bookAppointmentsRepo: getIt(), hmgServicesRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()), ); + getIt.registerLazySingleton( + () => BloodDonationViewModel(bloodDonationRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt()), + ); + // Screen-specific VMs → Factory // getIt.registerFactory( // () => BookAppointmentsViewModel( diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index 852071f..df8cdcb 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -875,4 +875,54 @@ class Utils { Uri uri = Uri.parse(url); launchUrl(uri, mode: LaunchMode.inAppBrowserView); } + + + static Color getCardBorderColor(int currentQueueStatus) { + switch (currentQueueStatus) { + case 0: + return AppColors.ratingColorYellow; + case 1: + return AppColors.primaryRedColor; + case 2: + return AppColors.successColor; + } + return AppColors.textColor; + } + + static Color getCardButtonColor(int currentQueueStatus) { + switch (currentQueueStatus) { + case 0: + return AppColors.textColor.withValues(alpha: 0.08); + case 1: + return AppColors.primaryRedColor; + case 2: + return AppColors.successColor; + } + return AppColors.primaryRedColor; + } + + static Color getCardButtonTextColor(int currentQueueStatus) { + switch (currentQueueStatus) { + case 0: + return Color(0xFFA2A2A2); + case 1: + return AppColors.whiteColor; + case 2: + return AppColors.whiteColor; + } + return AppColors.primaryRedColor; + } + + static String getCardButtonText(int currentQueueStatus) { + switch (currentQueueStatus) { + case 0: + return "Please wait! you will be called for vital signs".needTranslation; + case 1: + return "Please visit Room S5 for vital signs".needTranslation; + case 2: + return "Please visit Room S5 to the Doctor".needTranslation; + } + return ""; + } + } diff --git a/lib/features/blood_donation/blood_donation_repo.dart b/lib/features/blood_donation/blood_donation_repo.dart new file mode 100644 index 0000000..84997b2 --- /dev/null +++ b/lib/features/blood_donation/blood_donation_repo.dart @@ -0,0 +1,95 @@ +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/common_models/generic_api_model.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_response_model.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/models/cities_model.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; + +abstract class BloodDonationRepo { + Future>>> getAllCities(); + + Future>> getPatientBloodGroupDetails(); +} + +class BloodDonationRepoImp implements BloodDonationRepo { + final ApiClient apiClient; + final LoggerService loggerService; + + BloodDonationRepoImp({required this.loggerService, required this.apiClient}); + + @override + Future>>> getAllCities() async { + Map mapDevice = {}; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_CITIES_REQUEST, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['ListCities']; + final citiesList = list.map((item) => CitiesModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: citiesList, + ); + } 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>> getPatientBloodGroupDetails() async { + Map mapDevice = {}; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + GET_BLOOD_REQUEST, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['List_BloodGroupDetails'][0]; + final patientBloodGroup = List_BloodGroupDetailsModel.fromJson(list); + + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: patientBloodGroup, + ); + } 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())); + } + } +} \ No newline at end of file diff --git a/lib/features/blood_donation/blood_donation_view_model.dart b/lib/features/blood_donation/blood_donation_view_model.dart new file mode 100644 index 0000000..7d2e5df --- /dev/null +++ b/lib/features/blood_donation/blood_donation_view_model.dart @@ -0,0 +1,133 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_repo.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_list_model.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_response_model.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/models/cities_model.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; +import 'package:hmg_patient_app_new/services/error_handler_service.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; + +class BloodDonationViewModel extends ChangeNotifier { + final DialogService dialogService; + BloodDonationRepo bloodDonationRepo; + ErrorHandlerService errorHandlerService; + final NavigationService navigationService; + final AppState appState; + + List citiesList = []; + List bloodGroupList = [ + BloodGroupListModel("O+", 0), + BloodGroupListModel("O-", 1), + BloodGroupListModel("AB+", 2), + BloodGroupListModel("AB-", 3), + BloodGroupListModel("A+", 4), + BloodGroupListModel("A-", 5), + BloodGroupListModel("B+", 6), + BloodGroupListModel("B-", 7), + ]; + + late CitiesModel selectedCity; + late BloodGroupListModel selectedBloodGroup; + int _selectedHospitalIndex = 0; + int _selectedBloodTypeIndex = 0; + String _selectedBloodType = ''; + + List_BloodGroupDetailsModel patientBloodGroupDetailsModel = List_BloodGroupDetailsModel(); + + BloodDonationViewModel({required this.bloodDonationRepo, required this.errorHandlerService, required this.navigationService, required this.dialogService, required this.appState}); + + setSelectedCity(CitiesModel city) { + selectedCity = city; + notifyListeners(); + } + + Future getRegionSelectedClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async { + citiesList.clear(); + selectedCity = CitiesModel(); + notifyListeners(); + final result = await bloodDonationRepo.getAllCities(); + + result.fold( + (failure) async { + onError!(failure.message); + }, + (apiResponse) { + if (apiResponse.messageStatus == 2) { + onError!(apiResponse.errorMessage ?? 'An unexpected error occurred'); + } else if (apiResponse.messageStatus == 1) { + citiesList = apiResponse.data!; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + Future getPatientBloodGroupDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await bloodDonationRepo.getPatientBloodGroupDetails(); + + result.fold( + (failure) async { + onError!(failure.message); + }, + (apiResponse) { + if (apiResponse.messageStatus == 2) { + onError!(apiResponse.errorMessage ?? 'An unexpected error occurred'); + } else if (apiResponse.messageStatus == 1) { + patientBloodGroupDetailsModel = apiResponse.data!; + + CitiesModel citiesModel = CitiesModel(); + citiesModel.iD = getSelectedCityID(); + _selectedHospitalIndex = (citiesModel.iD! - 1); + citiesModel.description = citiesList[_selectedHospitalIndex].description; + citiesModel.descriptionN = citiesList[_selectedHospitalIndex].descriptionN; + selectedCity = citiesModel; + _selectedBloodType = patientBloodGroupDetailsModel.bloodGroup!; + _selectedBloodTypeIndex = getBloodIndex(_selectedBloodType); + + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + int getSelectedCityID() { + int cityID = 1; + citiesList.forEach((element) { + if (element.description == patientBloodGroupDetailsModel.city) { + cityID = element.iD!; + } + }); + return cityID; + } + + int getBloodIndex(String type) { + switch (type) { + case "O+": + return 0; + case "O-": + return 1; + case "AB+": + return 2; + case "AB-": + return 3; + case "A+": + return 4; + case "A-": + return 5; + case "B+": + return 6; + case "B-": + return 7; + + default: + return 0; + } + } +} diff --git a/lib/features/blood_donation/models/blood_group_list_model.dart b/lib/features/blood_donation/models/blood_group_list_model.dart new file mode 100644 index 0000000..9a98cc5 --- /dev/null +++ b/lib/features/blood_donation/models/blood_group_list_model.dart @@ -0,0 +1,6 @@ +class BloodGroupListModel { + String name; + int value; + + BloodGroupListModel(this.name, this.value); +} diff --git a/lib/features/blood_donation/models/blood_group_response_model.dart b/lib/features/blood_donation/models/blood_group_response_model.dart new file mode 100644 index 0000000..ceaca78 --- /dev/null +++ b/lib/features/blood_donation/models/blood_group_response_model.dart @@ -0,0 +1,61 @@ +class List_BloodGroupDetailsModel { + int? iD; + int? patientID; + int? patientType; + bool? patientOutSA; + int? zipCode; + String? cellNumber; + String? cityCode; + String? city; + int? gender; + String? bloodGroup; + String? nationalID; + bool? isActive; + + List_BloodGroupDetailsModel({ + this.iD, + this.patientID, + this.patientType, + this.patientOutSA, + this.zipCode, + this.cellNumber, + this.cityCode, + this.city, + this.gender, + this.bloodGroup, + this.nationalID, + this.isActive, + }); + + List_BloodGroupDetailsModel.fromJson(Map json) { + iD = json['ID']; + patientID = json['PatientID']; + patientType = json['PatientType']; + patientOutSA = json['PatientOutSA']; + zipCode = json['ZipCode']; + cellNumber = json['CellNumber']; + cityCode = json['CityCode']; + city = json['City']; + gender = json['Gender']; + bloodGroup = json['BloodGroup']; + nationalID = json['NationalID']; + isActive = json['IsActive']; + } + + Map toJson() { + final Map data = new Map(); + data['ID'] = this.iD; + data['PatientID'] = this.patientID; + data['PatientType'] = this.patientType; + data['PatientOutSA'] = this.patientOutSA; + data['ZipCode'] = this.zipCode; + data['CellNumber'] = this.cellNumber; + data['CityCode'] = this.cityCode; + data['City'] = this.city; + data['Gender'] = this.gender; + data['BloodGroup'] = this.bloodGroup; + data['NationalID'] = this.nationalID; + data['IsActive'] = this.isActive; + return data; + } +} diff --git a/lib/features/blood_donation/models/cities_model.dart b/lib/features/blood_donation/models/cities_model.dart new file mode 100644 index 0000000..cf7e631 --- /dev/null +++ b/lib/features/blood_donation/models/cities_model.dart @@ -0,0 +1,21 @@ +class CitiesModel { + int? iD; + String? description; + String? descriptionN; + + CitiesModel({this.iD, this.description, this.descriptionN}); + + CitiesModel.fromJson(Map json) { + iD = json['ID']; + description = json['Description']; + descriptionN = json['DescriptionN']; + } + + Map toJson() { + final Map data = new Map(); + data['ID'] = this.iD; + data['Description'] = this.description; + data['DescriptionN'] = this.descriptionN; + return data; + } +} \ No newline at end of file diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index 2413827..d96cb4f 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -497,8 +497,8 @@ class BookAppointmentsViewModel extends ChangeNotifier { } initialSlotDuration = apiResponse.data["InitialSlotDuration"]; freeSlotsResponse = apiResponse.data['FreeTimeSlots']; - // isWaitingAppointmentAvailable = apiResponse.data["IsAllowToBookWaitingAppointment"]; - isWaitingAppointmentAvailable = true; + isWaitingAppointmentAvailable = apiResponse.data["IsAllowToBookWaitingAppointment"]; + // isWaitingAppointmentAvailable = true; freeSlotsResponse.forEach((element) { // date = (isLiveCareSchedule != null && isLiveCareSchedule) diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index fa1ad22..71e0312 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -37,6 +37,9 @@ class MyAppointmentsViewModel extends ChangeNotifier { DateTime? start = null; DateTime? end = null; + bool isPatientHasQueueAppointment = false; + int currentQueueStatus = 0; + List patientAppointmentsHistoryList = []; List filteredAppointmentList = []; @@ -85,6 +88,12 @@ class MyAppointmentsViewModel extends ChangeNotifier { isTamaraDetailsLoading = true; isAppointmentPatientShareLoading = true; isEyeMeasurementsAppointmentsLoading = true; + isPatientHasQueueAppointment = false; + notifyListeners(); + } + + setCurrentQueueStatus(int currentQueueStatus) { + this.currentQueueStatus = currentQueueStatus; notifyListeners(); } diff --git a/lib/main.dart b/lib/main.dart index 1af80b6..6a065fe 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,6 +9,7 @@ import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart'; import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart'; @@ -145,6 +146,9 @@ void main() async { ), ChangeNotifierProvider( create: (_) => getIt.get(), + ), + ChangeNotifierProvider( + create: (_) => getIt.get(), ) ], child: MyApp()), ), diff --git a/lib/presentation/appointments/appointment_queue_page.dart b/lib/presentation/appointments/appointment_queue_page.dart index c2765a2..f48f1eb 100644 --- a/lib/presentation/appointments/appointment_queue_page.dart +++ b/lib/presentation/appointments/appointment_queue_page.dart @@ -6,261 +6,215 @@ 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/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; +import 'package:provider/provider.dart'; class AppointmentQueuePage extends StatelessWidget { AppointmentQueuePage({super.key}); AppState? appState; - int currentQueueStatus = 0; @override Widget build(BuildContext context) { - currentQueueStatus = 1; appState = getIt.get(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, - body: Column( - children: [ - Expanded( - child: CollapsingListView( - title: "Queueing".needTranslation, - child: SingleChildScrollView( - child: Padding( - padding: EdgeInsets.all(24.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: false, - side: BorderSide(color: getCardBorderColor(), width: 2.w), - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - AppCustomChipWidget( - labelText: currentQueueStatus == 0 ? "In Queue".needTranslation : "Your Turn".needTranslation, - backgroundColor: getCardBorderColor().withValues(alpha: 0.20), - textColor: getCardBorderColor(), - ), - Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h), - ], - ), - SizedBox(height: 10.h), - "Hala ${appState!.getAuthenticatedUser()!.firstName}!!!".needTranslation.toText16(isBold: true), - SizedBox(height: 8.h), - "Thank you for your patience, here is your queue number.".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), - SizedBox(height: 8.h), - "IMD W-A-2".needTranslation.toText32(isBold: true), - SizedBox(height: 8.h), - CustomButton( - text: getCardButtonText(), - onPressed: () {}, - backgroundColor: getCardButtonColor(), - borderColor: getCardButtonColor().withValues(alpha: 0.01), - textColor: getCardButtonTextColor(), - fontSize: 12.f, - fontWeight: FontWeight.w600, - borderRadius: 12.r, - padding: EdgeInsets.symmetric(horizontal: 10.w), - height: 40.h, - iconColor: AppColors.whiteColor, - iconSize: 18.h, - ), - ], + body: Consumer(builder: (context, myAppointmentsVM, child) { + return Column( + children: [ + Expanded( + child: CollapsingListView( + title: "Queueing".needTranslation, + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.all(24.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: false, + side: BorderSide(color: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus), width: 2.w), ), - ), - ), - SizedBox(height: 16.h), - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: true, - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "Serving Now".needTranslation.toText16(isBold: true), - SizedBox(height: 18.h), - ListView.separated( - padding: EdgeInsets.zero, - shrinkWrap: true, - itemCount: 3, - physics: NeverScrollableScrollPhysics(), - itemBuilder: (BuildContext context, int index) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "IMD W-A-2".needTranslation.toText17(isBold: true), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - "Room: S2".toText12(fontWeight: FontWeight.w500), - SizedBox(width: 8.w), - AppCustomChipWidget( - deleteIcon: AppAssets.call_for_vitals, - labelText: "Call for vital signs".needTranslation, - iconColor: AppColors.primaryRedColor, - textColor: AppColors.primaryRedColor, - iconSize: 14.w, - backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1), - labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: -2.h), - ), - ], - ), - ], - ); - }, - separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 8.h), - ), - ], + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AppCustomChipWidget( + labelText: myAppointmentsVM.currentQueueStatus == 0 ? "In Queue".needTranslation : "Your Turn".needTranslation, + backgroundColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus).withValues(alpha: 0.20), + textColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus), + ), + Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h), + ], + ), + SizedBox(height: 10.h), + "Hala ${appState!.getAuthenticatedUser()!.firstName}!!!".needTranslation.toText16(isBold: true), + SizedBox(height: 8.h), + "Thank you for your patience, here is your queue number.".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + SizedBox(height: 8.h), + "IMD W-A-5".needTranslation.toText32(isBold: true), + SizedBox(height: 8.h), + CustomButton( + text: Utils.getCardButtonText(myAppointmentsVM.currentQueueStatus), + onPressed: () {}, + backgroundColor: Utils.getCardButtonColor(myAppointmentsVM.currentQueueStatus), + borderColor: Utils.getCardButtonColor(myAppointmentsVM.currentQueueStatus).withValues(alpha: 0.01), + textColor: Utils.getCardButtonTextColor(myAppointmentsVM.currentQueueStatus), + fontSize: 12.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 40.h, + iconColor: AppColors.whiteColor, + iconSize: 18.h, + ), + ], + ), ), ), - ), - SizedBox(height: 16.h), - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: true, + SizedBox(height: 16.h), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Serving Now".needTranslation.toText16(isBold: true), + SizedBox(height: 18.h), + ListView.separated( + padding: EdgeInsets.zero, + shrinkWrap: true, + itemCount: 3, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "IMD W-A-2".needTranslation.toText17(isBold: true), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + "Room: S2".toText12(fontWeight: FontWeight.w500), + SizedBox(width: 8.w), + AppCustomChipWidget( + deleteIcon: AppAssets.call_for_vitals, + labelText: "Call for vital signs".needTranslation, + iconColor: AppColors.primaryRedColor, + textColor: AppColors.primaryRedColor, + iconSize: 14.w, + backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1), + labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: -2.h), + ), + ], + ), + ], + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 8.h), + ), + ], + ), + ), ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.bulb_icon, width: 24.w, height: 24.h), - SizedBox(width: 8.w), - "Things to ask your doctor today".needTranslation.toText16(isBold: true), - ], - ), - SizedBox(height: 8.h), + SizedBox(height: 16.h), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.bulb_icon, width: 24.w, height: 24.h), + SizedBox(width: 8.w), + "Things to ask your doctor today".needTranslation.toText16(isBold: true), + ], + ), + SizedBox(height: 8.h), - // What can I do to improve my overall health? - // Are there any routine screenings I should get? - // What is this medication for? - // Are there any side effects I should know about? - // When should I come back for a follow-up? + // What can I do to improve my overall health? + // Are there any routine screenings I should get? + // What is this medication for? + // Are there any side effects I should know about? + // When should I come back for a follow-up? - "• ${"What can I do to improve my overall health?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), - SizedBox(height: 4.h), - "• ${"Are there any routine screenings I should get?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), - SizedBox(height: 4.h), - "• ${"What is this medication for?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), - SizedBox(height: 4.h), - "• ${"Are there any side effects I should know about?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), - SizedBox(height: 4.h), - "• ${"When should I come back for a follow-up?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + "• ${"What can I do to improve my overall health?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + SizedBox(height: 4.h), + "• ${"Are there any routine screenings I should get?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + SizedBox(height: 4.h), + "• ${"What is this medication for?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + SizedBox(height: 4.h), + "• ${"Are there any side effects I should know about?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + SizedBox(height: 4.h), + "• ${"When should I come back for a follow-up?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), - SizedBox(height: 16.h), - ], + SizedBox(height: 16.h), + ], + ), ), ), - ), - ], + ], + ), ), ), ), ), - ), - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.r, - hasShadow: true, - ), - child: CustomButton( - text: "Go to homepage".needTranslation, - onPressed: () { - Navigator.pushAndRemoveUntil( - context, - CustomPageRoute( - page: LandingNavigation(), - ), - (r) => false); - }, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedColor, - textColor: AppColors.whiteColor, - fontSize: 16.f, - fontWeight: FontWeight.w500, - borderRadius: 12.r, - padding: EdgeInsets.symmetric(horizontal: 10.w), - height: 50.h, - icon: AppAssets.homeBottom, - iconColor: AppColors.whiteColor, - iconSize: 18.h, - ).paddingSymmetrical(16.h, 24.h), - ) - ], - ), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: true, + ), + child: CustomButton( + text: "Go to homepage".needTranslation, + onPressed: () { + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + fontSize: 16.f, + fontWeight: FontWeight.w500, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 50.h, + icon: AppAssets.homeBottom, + iconColor: AppColors.whiteColor, + iconSize: 18.h, + ).paddingSymmetrical(16.h, 24.h), + ) + ], + ); + }), ); } - - Color getCardBorderColor() { - switch (currentQueueStatus) { - case 0: - return AppColors.ratingColorYellow; - case 1: - return AppColors.primaryRedColor; - case 2: - return AppColors.successColor; - } - return AppColors.textColor; - } - - Color getCardButtonColor() { - switch (currentQueueStatus) { - case 0: - return AppColors.textColor.withValues(alpha: 0.08); - case 1: - return AppColors.primaryRedColor; - case 2: - return AppColors.successColor; - } - return AppColors.primaryRedColor; - } - - Color getCardButtonTextColor() { - switch (currentQueueStatus) { - case 0: - return Color(0xFFA2A2A2); - case 1: - return AppColors.whiteColor; - case 2: - return AppColors.whiteColor; - } - return AppColors.primaryRedColor; - } - - String getCardButtonText() { - switch (currentQueueStatus) { - case 0: - return "Please wait! you will be called for vital signs".needTranslation; - case 1: - return "Please visit Room S5 for vital signs".needTranslation; - case 2: - return "Please visit Room S5 to the Doctor".needTranslation; - } - return ""; - } } diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index a629944..e6b974e 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -17,7 +17,6 @@ import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_vie import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/appointment_details_page.dart'; -import 'package:hmg_patient_app_new/presentation/appointments/appointment_queue_page.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/ask_doctor_request_type_select.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/eye_measurement_details_page.dart'; @@ -27,7 +26,6 @@ import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; -import 'package:smooth_corner/smooth_corner.dart'; class AppointmentCard extends StatelessWidget { final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; @@ -133,7 +131,7 @@ class AppointmentCard extends StatelessWidget { width: 63.w, height: 63.h, fit: BoxFit.cover, - ).circle(100).toShimmer2(isShow: isLoading), + ).circle(100.r).toShimmer2(isShow: isLoading), Transform.translate( offset: Offset(0.0, -20.h), child: Container( @@ -149,7 +147,11 @@ class AppointmentCard extends StatelessWidget { ), child: Column( mainAxisAlignment: MainAxisAlignment.center, - children: [Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h), SizedBox(height: 2.h), "4.7".toText11(isBold: true, color: AppColors.textColor)], + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h), + SizedBox(height: 2.h), + "${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText11(isBold: true, color: AppColors.textColor), + ], ), ).circle(100).toShimmer2(isShow: isLoading), ), @@ -169,10 +171,14 @@ class AppointmentCard extends StatelessWidget { spacing: 3.h, runSpacing: 4.h, children: [ - AppCustomChipWidget(labelText: isLoading ? 'Cardiology' : patientAppointmentHistoryResponseModel.clinicName!) - .toShimmer2(isShow: isLoading), - AppCustomChipWidget(labelText: isLoading ? 'Olaya' : patientAppointmentHistoryResponseModel.projectName!) - .toShimmer2(isShow: isLoading), + AppCustomChipWidget( + labelText: isLoading + ? 'Cardiology' + : (patientAppointmentHistoryResponseModel.clinicName!.length > 15 + ? '${patientAppointmentHistoryResponseModel.clinicName!.substring(0, 12)}...' + : patientAppointmentHistoryResponseModel.clinicName!), + ).toShimmer2(isShow: isLoading), + AppCustomChipWidget(labelText: isLoading ? 'Olaya' : patientAppointmentHistoryResponseModel.projectName!).toShimmer2(isShow: isLoading), AppCustomChipWidget( icon: AppAssets.appointment_calendar_icon, labelText: isLoading @@ -292,41 +298,34 @@ class AppointmentCard extends StatelessWidget { if (isRecent) { return CustomButton( text: LocaleKeys.askDoctor.tr(context: context), - onPressed: () { - Navigator.of(context).push( - CustomPageRoute( - page: AppointmentQueuePage(), - ), - ); + onPressed: () async { + LoaderBottomSheet.showLoader(loadingText: "Checking doctor availability...".needTranslation); + await myAppointmentsViewModel.isDoctorAvailable( + projectID: patientAppointmentHistoryResponseModel.projectID, + doctorId: patientAppointmentHistoryResponseModel.doctorID, + clinicId: patientAppointmentHistoryResponseModel.clinicID, + onSuccess: (value) async { + if (value) { + await myAppointmentsViewModel.getAskDoctorRequestTypes(onSuccess: (val) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + title: LocaleKeys.askDoctor.tr(context: context), + child: AskDoctorRequestTypeSelect( + askDoctorRequestTypeList: myAppointmentsViewModel.askDoctorRequestTypeList, + myAppointmentsViewModel: myAppointmentsViewModel, + patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel, + ), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }); + } else { + print("Doctor is not available"); + } + }); }, - // onPressed: () async { - // LoaderBottomSheet.showLoader(loadingText: "Checking doctor availability...".needTranslation); - // await myAppointmentsViewModel.isDoctorAvailable( - // projectID: patientAppointmentHistoryResponseModel.projectID, - // doctorId: patientAppointmentHistoryResponseModel.doctorID, - // clinicId: patientAppointmentHistoryResponseModel.clinicID, - // onSuccess: (value) async { - // if (value) { - // await myAppointmentsViewModel.getAskDoctorRequestTypes(onSuccess: (val) { - // LoaderBottomSheet.hideLoader(); - // showCommonBottomSheetWithoutHeight( - // context, - // title: LocaleKeys.askDoctor.tr(context: context), - // child: AskDoctorRequestTypeSelect( - // askDoctorRequestTypeList: myAppointmentsViewModel.askDoctorRequestTypeList, - // myAppointmentsViewModel: myAppointmentsViewModel, - // patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel, - // ), - // callBackFunc: () {}, - // isFullScreen: false, - // isCloseButtonVisible: true, - // ); - // }); - // } else { - // print("Doctor is not available"); - // } - // }); - // }, backgroundColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, diff --git a/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart b/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart index 74ab6b7..f430447 100644 --- a/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart +++ b/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart @@ -13,6 +13,7 @@ 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/appointment_queue_page.dart'; import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; @@ -156,7 +157,9 @@ class AppointmentCheckinBottomSheet extends StatelessWidget { ), (r) => false); Navigator.of(context).push( - CustomPageRoute(page: MyAppointmentsPage()), + CustomPageRoute( + page: AppointmentQueuePage(), + ), ); }, isFullScreen: false); }, diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart index b2d74ca..96d538b 100644 --- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart +++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; @@ -50,8 +51,29 @@ class AppointmentDoctorCard extends StatelessWidget { height: 63.h, fit: BoxFit.cover, ).circle(100.r), - SizedBox(height: 12.h), - AppCustomChipWidget(icon: AppAssets.rating_icon, iconColor: AppColors.ratingColorYellow, labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"), + Transform.translate( + offset: Offset(0.0, -20.h), + child: Container( + width: 40.w, + height: 40.h, + decoration: BoxDecoration( + color: AppColors.whiteColor, + shape: BoxShape.circle, // Makes the container circular + border: Border.all( + color: AppColors.scaffoldBgColor, // Color of the border + width: 1.5.w, // Width of the border + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h), + SizedBox(height: 2.h), + "${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText11(isBold: true, color: AppColors.textColor), + ], + ), + ).circle(100), + ), ], ), SizedBox(width: 16.w), @@ -60,13 +82,16 @@ class AppointmentDoctorCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ patientAppointmentHistoryResponseModel.doctorNameObj!.toText16(isBold: true), + SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, spacing: 6.w, runSpacing: 6.h, children: [ AppCustomChipWidget( - labelText: patientAppointmentHistoryResponseModel.clinicName!, + labelText: (patientAppointmentHistoryResponseModel.clinicName!.length > 15 + ? '${patientAppointmentHistoryResponseModel.clinicName!.substring(0, 12)}...' + : patientAppointmentHistoryResponseModel.clinicName!), labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w), ), AppCustomChipWidget( @@ -75,8 +100,7 @@ class AppointmentDoctorCard extends StatelessWidget { ), AppCustomChipWidget( icon: AppAssets.doctor_calendar_icon, - labelText: - "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}, ${DateUtil.formatDateToTimeLang( + labelText: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang( DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false, )}", @@ -100,8 +124,7 @@ class AppointmentDoctorCard extends StatelessWidget { ), ], ), - SizedBox(height: 16.h), - + SizedBox(height: 8.h), Visibility( visible: renderWidgetForERDisplay == false, child: getAppointmentActionButtons( diff --git a/lib/presentation/appointments/widgets/appointment_queueing_screen.dart b/lib/presentation/appointments/widgets/appointment_queueing_screen.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/presentation/blood_donation/blood_donation_page.dart b/lib/presentation/blood_donation/blood_donation_page.dart new file mode 100644 index 0000000..8908652 --- /dev/null +++ b/lib/presentation/blood_donation/blood_donation_page.dart @@ -0,0 +1,172 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/utils/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/blood_donation/blood_donation_view_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/blood_donation/widgets/select_city_widget.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:provider/provider.dart'; + +class BloodDonationPage extends StatelessWidget { + BloodDonationPage({super.key}); + + late AppState appState; + + @override + Widget build(BuildContext context) { + appState = getIt.get(); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: Consumer(builder: (context, bloodDonationVM, child) { + return Column( + children: [ + Expanded( + child: CollapsingListView( + title: LocaleKeys.bloodDonation.tr(), + child: Padding( + padding: EdgeInsets.all(24.w), + child: SingleChildScrollView( + child: Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: false, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.select_city_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.w), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.city.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500), + (appState.isArabic() + ? (bloodDonationVM.selectedCity.descriptionN ?? LocaleKeys.select.tr()) + : bloodDonationVM.selectedCity.description ?? LocaleKeys.select.tr(context: context)) + .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + ], + ), + ], + ), + Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h), + ], + ).onPress(() async { + LoaderBottomSheet.showLoader(loadingText: "Fetching Cities..."); + await bloodDonationVM.getRegionSelectedClinics(onSuccess: (val) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight(context, + title: LocaleKeys.selectCity.tr(context: context), + isDismissible: true, + child: SelectCityWidget( + bloodDonationViewModel: bloodDonationVM, + ), + callBackFunc: () {}); + }, onError: (err) { + LoaderBottomSheet.hideLoader(); + }); + }), + 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.my_account_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.w), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.gender.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500), + "Male".toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + ], + ), + ], + ), + Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h), + ], + ), + 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.my_account_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.w), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.bloodType.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500), + "AB+".toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + ], + ), + ], + ), + Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h), + ], + ), + ], + ), + ), + ), + ), + ), + ), + ), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: true, + ), + child: SizedBox( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomButton( + text: LocaleKeys.save.tr(), + onPressed: () { + // openDoctorScheduleCalendar(); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + fontSize: 16.f, + fontWeight: FontWeight.w500, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 50.h, + iconSize: 18.h, + ).paddingSymmetrical(16.h, 24.h), + ], + ), + ), + ), + ], + ); + }), + ); + } +} diff --git a/lib/presentation/blood_donation/widgets/city_list_item.dart b/lib/presentation/blood_donation/widgets/city_list_item.dart new file mode 100644 index 0000000..408fb26 --- /dev/null +++ b/lib/presentation/blood_donation/widgets/city_list_item.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/models/cities_model.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; + +class CityListItem extends StatelessWidget { + final CitiesModel cityModel; + + late AppState appState; + + CityListItem({super.key, required this.cityModel}); + + @override + Widget build(BuildContext context) { + appState = getIt.get(); + return DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: false, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 8.h, + children: [hospitalName], + ), + ), + Transform.flip( + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, + iconColor: AppColors.blackColor, + width: 40.h, + height: 40.h, + fit: BoxFit.contain, + ), + ), + ], + ).paddingSymmetrical(16.h, 16.h), + ); + } + + Widget get hospitalName => Row( + children: [ + Expanded( + child: (appState.isArabic() ? cityModel.descriptionN : cityModel.description)!.toText16(color: AppColors.textColor, isBold: true), + ) + ], + ); +} diff --git a/lib/presentation/blood_donation/widgets/select_city_widget.dart b/lib/presentation/blood_donation/widgets/select_city_widget.dart new file mode 100644 index 0000000..a0e8477 --- /dev/null +++ b/lib/presentation/blood_donation/widgets/select_city_widget.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart'; +import 'package:hmg_patient_app_new/presentation/blood_donation/widgets/city_list_item.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; + +class SelectCityWidget extends StatelessWidget { + SelectCityWidget({super.key, required this.bloodDonationViewModel}); + + BloodDonationViewModel bloodDonationViewModel; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 8.h), + SizedBox( + height: MediaQuery.sizeOf(context).height * .4, + child: ListView.separated( + itemBuilder: (_, index) { + return CityListItem( + cityModel: bloodDonationViewModel.citiesList[index], + ).onPress(() { + bloodDonationViewModel.setSelectedCity(bloodDonationViewModel.citiesList[index]); + Navigator.of(context).pop(); + }); + }, + separatorBuilder: (_, __) => SizedBox( + height: 8.h, + ), + itemCount: bloodDonationViewModel.citiesList.length), + ) + ], + ); + } +} diff --git a/lib/presentation/habib_wallet/widgets/hospital_list_item.dart b/lib/presentation/habib_wallet/widgets/hospital_list_item.dart index ae47326..a46f79d 100644 --- a/lib/presentation/habib_wallet/widgets/hospital_list_item.dart +++ b/lib/presentation/habib_wallet/widgets/hospital_list_item.dart @@ -67,40 +67,4 @@ class HospitalListItemAdvancePayment extends StatelessWidget { ) ], ); - -// Widget get distanceInfo => Row( -// children: [ -// Visibility( -// visible: (hospitalModel.distanceInKMs != "0"), -// child: AppCustomChipWidget( -// labelText: "${hospitalData?.distanceInKMs ?? ""} km".needTranslation, -// deleteIcon: AppAssets.location_red, -// deleteIconSize: Size(9, 12), -// backgroundColor: AppColors.secondaryLightRedColor, -// textColor: AppColors.errorColor, -// ), -// ), -// Visibility( -// visible: (hospitalData?.distanceInKMs == "0"), -// child: Row( -// children: [ -// AppCustomChipWidget( -// labelText: "Distance not available".needTranslation, -// textColor: AppColors.blackColor, -// ), -// SizedBox( -// width: 8.h, -// ) -// ], -// )), -// Visibility( -// visible: !isLocationEnabled, -// child: AppCustomChipWidget( -// labelText: "Location turned off".needTranslation, -// deleteIcon: AppAssets.location_unavailable, -// deleteIconSize: Size(9, 12), -// textColor: AppColors.blackColor, -// )), -// ], -// ); } diff --git a/lib/presentation/hmg_services/services_page.dart b/lib/presentation/hmg_services/services_page.dart index fd6e976..1b9f3cd 100644 --- a/lib/presentation/hmg_services/services_page.dart +++ b/lib/presentation/hmg_services/services_page.dart @@ -42,6 +42,16 @@ class ServicesPage extends StatelessWidget { textColor: AppColors.blackColor, route: AppRoutes.homeHealthCarePage, ), + HmgServicesComponentModel( + 3, + "Blood Donation".needTranslation, + "".needTranslation, + AppAssets.emergency_services_icon, + true, + bgColor: AppColors.bgGreenColor, + textColor: AppColors.blackColor, + route: AppRoutes.bloodDonationPage, + ), HmgServicesComponentModel( 11, "Virtual Tour".needTranslation, diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 1b2ce9a..88b9274 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -266,11 +266,86 @@ class _LandingPageState extends State { builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor), ), itemBuilder: (BuildContext context, int index) { - return (immediateLiveCareVM.patientHasPendingLiveCareRequest && index == 0) - ? Column( - children: [ - SizedBox(height: 12.h), - Container( + return (myAppointmentsVM.isPatientHasQueueAppointment && index == 0) + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: false, + side: BorderSide(color: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus), width: 2.w), + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AppCustomChipWidget( + labelText: myAppointmentsVM.currentQueueStatus == 0 ? "In Queue".needTranslation : "Your Turn".needTranslation, + backgroundColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus).withValues(alpha: 0.20), + textColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus), + ), + Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h), + ], + ), + SizedBox(height: 10.h), + "Hala ${appState!.getAuthenticatedUser()!.firstName}!!!".needTranslation.toText16(isBold: true), + SizedBox(height: 8.h), + "Thank you for your patience, here is your queue number." + .needTranslation + .toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + SizedBox(height: 8.h), + "IMD W-A-5".needTranslation.toText28(isBold: true), + SizedBox(height: 12.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + "Serving Now: ".needTranslation.toText14(isBold: true), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + "IMD W-A-2".needTranslation.toText12(isBold: true), + SizedBox(width: 8.w), + AppCustomChipWidget( + deleteIcon: AppAssets.call_for_vitals, + labelText: "Call for vital signs".needTranslation, + iconColor: AppColors.primaryRedColor, + textColor: AppColors.primaryRedColor, + iconSize: 14.w, + backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1), + labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: -2.h), + ), + ], + ), + ], + ), + SizedBox(height: 8.h), + CustomButton( + text: Utils.getCardButtonText(myAppointmentsVM.currentQueueStatus), + onPressed: () {}, + backgroundColor: Utils.getCardButtonColor(myAppointmentsVM.currentQueueStatus), + borderColor: Utils.getCardButtonColor(myAppointmentsVM.currentQueueStatus).withValues(alpha: 0.01), + textColor: Utils.getCardButtonTextColor(myAppointmentsVM.currentQueueStatus), + fontSize: 12.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 40.h, + iconColor: AppColors.whiteColor, + iconSize: 18.h, + ), + ], + ), + ), + ) + : (immediateLiveCareVM.patientHasPendingLiveCareRequest && index == 0) + ? Column( + children: [ + SizedBox(height: 12.h), + Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 20.r, diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart index ed5c0db..d09a6c0 100644 --- a/lib/routes/app_routes.dart +++ b/lib/routes/app_routes.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; import 'package:hmg_patient_app_new/presentation/authentication/register.dart'; import 'package:hmg_patient_app_new/presentation/authentication/register_step2.dart'; +import 'package:hmg_patient_app_new/presentation/blood_donation/blood_donation_page.dart'; import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/comprehensive_checkup_page.dart'; import 'package:hmg_patient_app_new/presentation/e_referral/new_e_referral.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; @@ -21,6 +22,7 @@ class AppRoutes { static const String comprehensiveCheckupPage = '/comprehensiveCheckupPage'; static const String homeHealthCarePage = '/homeHealthCarePage'; static const String zoomCallPage = '/zoomCallPage'; + static const String bloodDonationPage = '/bloodDonationPage'; static Map get routes => { initialRoute: (context) => SplashPage(), @@ -32,6 +34,7 @@ class AppRoutes { eReferralPage: (context) => NewReferralPage(), comprehensiveCheckupPage: (context) => ComprehensiveCheckupPage(), homeHealthCarePage: (context) => HhcProceduresPage(), - zoomCallPage: (context) => CallScreen() + zoomCallPage: (context) => CallScreen(), + bloodDonationPage: (context) => BloodDonationPage() }; }