From 712e11c69aa5657f499d97780476cc06e0b12a42 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Wed, 3 Sep 2025 17:26:54 +0300 Subject: [PATCH] prescription details page implementation contd. --- assets/images/svg/doctor_calendar_icon.svg | 8 + .../images/svg/prescription_remarks_icon.svg | 5 + assets/images/svg/rating_icon.svg | 3 + lib/core/app_assets.dart | 3 + .../prescription_detail_response_model.dart | 144 ++++++ .../prescriptions/prescriptions_repo.dart | 66 +++ .../prescriptions_view_model.dart | 34 +- .../prescription_detail_page.dart | 313 ++++++++++++ .../prescriptions_list_page.dart | 466 +++++++++--------- lib/theme/colors.dart | 1 + 10 files changed, 812 insertions(+), 231 deletions(-) create mode 100644 assets/images/svg/doctor_calendar_icon.svg create mode 100644 assets/images/svg/prescription_remarks_icon.svg create mode 100644 assets/images/svg/rating_icon.svg create mode 100644 lib/features/prescriptions/models/resp_models/prescription_detail_response_model.dart create mode 100644 lib/presentation/prescriptions/prescription_detail_page.dart diff --git a/assets/images/svg/doctor_calendar_icon.svg b/assets/images/svg/doctor_calendar_icon.svg new file mode 100644 index 0000000..f76c49f --- /dev/null +++ b/assets/images/svg/doctor_calendar_icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/images/svg/prescription_remarks_icon.svg b/assets/images/svg/prescription_remarks_icon.svg new file mode 100644 index 0000000..a8d7adb --- /dev/null +++ b/assets/images/svg/prescription_remarks_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/rating_icon.svg b/assets/images/svg/rating_icon.svg new file mode 100644 index 0000000..dee25d4 --- /dev/null +++ b/assets/images/svg/rating_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index a43406e..bd3e541 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -64,6 +64,9 @@ class AppAssets { static const String view_report_icon = '$svgBasePath/view_report_icon.svg'; static const String forward_arrow_icon = '$svgBasePath/forward_arrow_icon.svg'; static const String prescription_refill_icon = '$svgBasePath/prescription_refill_icon.svg'; + static const String rating_icon = '$svgBasePath/rating_icon.svg'; + static const String doctor_calendar_icon = '$svgBasePath/doctor_calendar_icon.svg'; + static const String prescription_remarks_icon = '$svgBasePath/prescription_remarks_icon.svg'; //bottom navigation// diff --git a/lib/features/prescriptions/models/resp_models/prescription_detail_response_model.dart b/lib/features/prescriptions/models/resp_models/prescription_detail_response_model.dart new file mode 100644 index 0000000..3ca8396 --- /dev/null +++ b/lib/features/prescriptions/models/resp_models/prescription_detail_response_model.dart @@ -0,0 +1,144 @@ +class PrescriptionDetailResponseModel { + String? address; + num? appointmentNo; + String? clinic; + dynamic companyName; + num? days; + String? doctorName; + num? doseDailyQuantity; + String? frequency; + num? frequencyNumber; + dynamic image; + dynamic imageExtension; + String? imageSRCUrl; + dynamic imageString; + String? imageThumbUrl; + String? isCovered; + String? itemDescription; + num? itemID; + String? orderDate; + num? patientID; + String? patientName; + String? phoneOffice1; + dynamic prescriptionQR; + num? prescriptionTimes; + dynamic productImage; + dynamic productImageBase64; + String? productImageString; + num? projectID; + String? projectName; + String? remarks; + String? route; + String? sKU; + num? scaleOffset; + String? startDate; + + PrescriptionDetailResponseModel( + {this.address, + this.appointmentNo, + this.clinic, + this.companyName, + this.days, + this.doctorName, + this.doseDailyQuantity, + this.frequency, + this.frequencyNumber, + this.image, + this.imageExtension, + this.imageSRCUrl, + this.imageString, + this.imageThumbUrl, + this.isCovered, + this.itemDescription, + this.itemID, + this.orderDate, + this.patientID, + this.patientName, + this.phoneOffice1, + this.prescriptionQR, + this.prescriptionTimes, + this.productImage, + this.productImageBase64, + this.productImageString, + this.projectID, + this.projectName, + this.remarks, + this.route, + this.sKU, + this.scaleOffset, + this.startDate}); + + PrescriptionDetailResponseModel.fromJson(Map json) { + address = json['Address']; + appointmentNo = json['AppointmentNo']; + clinic = json['Clinic']; + companyName = json['CompanyName']; + days = json['Days']; + doctorName = json['DoctorName']; + doseDailyQuantity = json['DoseDailyQuantity']; + frequency = json['Frequency']; + frequencyNumber = json['FrequencyNumber']; + image = json['Image']; + imageExtension = json['ImageExtension']; + imageSRCUrl = json['ImageSRCUrl']; + imageString = json['ImageString']; + imageThumbUrl = json['ImageThumbUrl']; + isCovered = json['IsCovered']; + itemDescription = json['ItemDescription']; + itemID = json['ItemID']; + orderDate = json['OrderDate']; + patientID = json['PatientID']; + patientName = json['PatientName']; + phoneOffice1 = json['PhoneOffice1']; + prescriptionQR = json['PrescriptionQR']; + prescriptionTimes = json['PrescriptionTimes']; + productImage = json['ProductImage']; + productImageBase64 = json['ProductImageBase64']; + productImageString = json['ProductImageString']; + projectID = json['ProjectID']; + projectName = json['ProjectName']; + remarks = json['Remarks']; + route = json['Route']; + sKU = json['SKU']; + scaleOffset = json['ScaleOffset']; + startDate = json['StartDate']; + } + + Map toJson() { + final Map data = new Map(); + data['Address'] = address; + data['AppointmentNo'] = appointmentNo; + data['Clinic'] = clinic; + data['CompanyName'] = companyName; + data['Days'] = days; + data['DoctorName'] = doctorName; + data['DoseDailyQuantity'] = doseDailyQuantity; + data['Frequency'] = frequency; + data['FrequencyNumber'] = frequencyNumber; + data['Image'] = image; + data['ImageExtension'] = imageExtension; + data['ImageSRCUrl'] = imageSRCUrl; + data['ImageString'] = imageString; + data['ImageThumbUrl'] = imageThumbUrl; + data['IsCovered'] = isCovered; + data['ItemDescription'] = itemDescription; + data['ItemID'] = itemID; + data['OrderDate'] = orderDate; + data['PatientID'] = patientID; + data['PatientName'] = patientName; + data['PhoneOffice1'] = phoneOffice1; + data['PrescriptionQR'] = prescriptionQR; + data['PrescriptionTimes'] = prescriptionTimes; + data['ProductImage'] = productImage; + data['ProductImageBase64'] = productImageBase64; + data['ProductImageString'] = productImageString; + data['ProjectID'] = projectID; + data['ProjectName'] = projectName; + data['Remarks'] = remarks; + data['Route'] = route; + data['SKU'] = sKU; + data['ScaleOffset'] = scaleOffset; + data['StartDate'] = startDate; + return data; + } +} diff --git a/lib/features/prescriptions/prescriptions_repo.dart b/lib/features/prescriptions/prescriptions_repo.dart index 53cf827..de7bc4b 100644 --- a/lib/features/prescriptions/prescriptions_repo.dart +++ b/lib/features/prescriptions/prescriptions_repo.dart @@ -4,10 +4,13 @@ import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; import 'package:dartz/dartz.dart'; import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart'; +import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/prescription_detail_response_model.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; abstract class PrescriptionsRepo { Future>>> getPatientPrescriptionOrders({required String patientId}); + + Future>>> getPatientPrescriptionDetails({required PatientPrescriptionsResponseModel prescriptionsResponseModel}); } class PrescriptionsRepoImp implements PrescriptionsRepo { @@ -72,4 +75,67 @@ class PrescriptionsRepoImp implements PrescriptionsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>>> getPatientPrescriptionDetails({required PatientPrescriptionsResponseModel prescriptionsResponseModel}) async { + final mapDevice = { + "AppointmentNo": prescriptionsResponseModel.appointmentNo.toString(), + "SetupID": prescriptionsResponseModel.setupID, + "EpisodeID": prescriptionsResponseModel.episodeID.toString(), + "ClinicID": prescriptionsResponseModel.clinicID.toString(), + "ProjectID": prescriptionsResponseModel.projectID.toString(), + "DischargeNo": prescriptionsResponseModel.dischargeNo.toString(), + "isDentalAllowedBackend": false, + "VersionID": 50.0, + "Channel": 3, + "LanguageID": 2, + "IPAdress": "10.20.10.20", + "generalid": "Cs2020@2016\$2958", + "Latitude": 0.0, + "Longitude": 0.0, + "DeviceTypeID": 1, + "PatientType": 1, + "PatientTypeID": 1, + "TokenID": "@dm!n", + "PatientID": "1018977", + "PatientOutSA": "0", + "SessionID": "03478TYC02N80874CTYN04883475!?" + }; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + prescriptionsResponseModel.isInOutPatient! ? GET_PRESCRIPTION_REPORT_ENH : GET_PRESCRIPTION_REPORT, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus}) { + try { + final list = prescriptionsResponseModel.isInOutPatient! ? response['ListPRM'] : response['INP_GetPrescriptionReport_List']; + if (list == null || list.isEmpty) { + throw Exception("prescription list is empty"); + } + + final prescriptionOrders = list.map((item) => PrescriptionDetailResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: prescriptionOrders, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/prescriptions/prescriptions_view_model.dart b/lib/features/prescriptions/prescriptions_view_model.dart index 031b26e..23a818e 100644 --- a/lib/features/prescriptions/prescriptions_view_model.dart +++ b/lib/features/prescriptions/prescriptions_view_model.dart @@ -1,21 +1,26 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart'; +import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/prescription_detail_response_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; class PrescriptionsViewModel extends ChangeNotifier { bool isPrescriptionsOrdersLoading = false; + bool isPrescriptionsDetailsLoading = false; PrescriptionsRepo prescriptionsRepo; ErrorHandlerService errorHandlerService; + // Prescription Orders Lists List patientPrescriptionOrders = []; List patientPrescriptionOrdersByClinic = []; List patientPrescriptionOrdersByHospital = []; - List patientPrescriptionOrdersViewList = []; + // Prescription Details List + List prescriptionDetailsList = []; + bool isSortByClinic = true; PrescriptionsViewModel({required this.prescriptionsRepo, required this.errorHandlerService}); @@ -26,11 +31,18 @@ class PrescriptionsViewModel extends ChangeNotifier { patientPrescriptionOrdersByHospital.clear(); patientPrescriptionOrdersViewList.clear(); isPrescriptionsOrdersLoading = true; + isPrescriptionsDetailsLoading = true; isSortByClinic = true; getPatientPrescriptionOrders(); notifyListeners(); } + setPrescriptionsDetailsLoading() { + isPrescriptionsDetailsLoading = true; + prescriptionDetailsList.clear(); + notifyListeners(); + } + setIsSortByClinic(bool value) { isSortByClinic = value; if (isSortByClinic) { @@ -79,4 +91,24 @@ class PrescriptionsViewModel extends ChangeNotifier { }, ); } + + Future getPrescriptionDetails(PatientPrescriptionsResponseModel prescriptionsResponseModel, {Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await prescriptionsRepo.getPatientPrescriptionDetails(prescriptionsResponseModel: prescriptionsResponseModel); + + 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) { + prescriptionDetailsList = apiResponse.data!; + isPrescriptionsDetailsLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } } diff --git a/lib/presentation/prescriptions/prescription_detail_page.dart b/lib/presentation/prescriptions/prescription_detail_page.dart new file mode 100644 index 0000000..0231c67 --- /dev/null +++ b/lib/presentation/prescriptions/prescription_detail_page.dart @@ -0,0 +1,313 @@ +import 'dart:async'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/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/prescriptions/models/resp_models/patient_prescriptions_response_model.dart'; +import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; +import 'package:provider/provider.dart'; + +class PrescriptionDetailPage extends StatefulWidget { + PrescriptionDetailPage({super.key, required this.prescriptionsResponseModel}); + + PatientPrescriptionsResponseModel prescriptionsResponseModel; + + @override + State createState() => _PrescriptionDetailPageState(); +} + +class _PrescriptionDetailPageState extends State { + late PrescriptionsViewModel prescriptionsViewModel; + + @override + void initState() { + scheduleMicrotask(() { + prescriptionsViewModel.getPrescriptionDetails(widget.prescriptionsResponseModel); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + prescriptionsViewModel = Provider.of(context, listen: false); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + appBar: AppBar( + title: LocaleKeys.prescriptions.tr(context: context).toText18(), + backgroundColor: AppColors.bgScaffoldColor, + ), + body: SingleChildScrollView( + child: Consumer(builder: (context, prescriptionVM, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.prescriptions.tr(context: context).toText24(isBold: true).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 24.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( + mainAxisSize: MainAxisSize.min, + children: [ + Image.network( + widget.prescriptionsResponseModel.doctorImageURL!, + width: 24.h, + height: 24.h, + fit: BoxFit.fill, + ).circle(100), + SizedBox(width: 8.h), + Expanded(child: widget.prescriptionsResponseModel.doctorName!.toText16(isBold: true)), + ], + ), + SizedBox(height: 16.h), + Wrap( + direction: Axis.horizontal, + spacing: 6.h, + runSpacing: 6.h, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + icon: AppAssets.doctor_calendar_icon, + iconColor: AppColors.textColor, + iconSize: 13.h, + text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.prescriptionsResponseModel.appointmentDate), false), + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + text: widget.prescriptionsResponseModel.clinicDescription!, + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + icon: AppAssets.rating_icon, + iconColor: AppColors.ratingColorYellow, + iconSize: 13.h, + text: "Rating: ${widget.prescriptionsResponseModel.decimalDoctorRate}", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + text: widget.prescriptionsResponseModel.name!, + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + ], + ), + ], + ), + ), + ).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 16.h), + ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: prescriptionVM.isPrescriptionsDetailsLoading ? 5 : prescriptionVM.prescriptionDetailsList.length, + itemBuilder: (context, index) { + return prescriptionVM.isPrescriptionsDetailsLoading + ? const MoviesShimmerWidget() + : AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: AnimatedContainer( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + margin: EdgeInsets.symmetric(vertical: 8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.all(16.h), + child: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Image.network( + prescriptionVM.prescriptionDetailsList[index].imageSRCUrl!, + width: 60.h, + height: 60.h, + fit: BoxFit.fill, + ).circle(100), + SizedBox(width: 8.h), + Expanded( + child: prescriptionVM.prescriptionDetailsList[index].itemDescription!.toText16(isBold: true, maxlines: 2), + ), + ], + ), + SizedBox(height: 16.h), + Wrap( + direction: Axis.horizontal, + spacing: 6.h, + runSpacing: 6.h, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + text: "${LocaleKeys.route.tr(context: context)}: ${prescriptionVM.prescriptionDetailsList[index].route}", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + text: "${LocaleKeys.frequency.tr(context: context)}: ${prescriptionVM.prescriptionDetailsList[index].frequency}", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + text: "${LocaleKeys.dailyDoses.tr(context: context)}: ${prescriptionVM.prescriptionDetailsList[index].doseDailyQuantity}", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + text: "${LocaleKeys.days.tr(context: context)}: ${prescriptionVM.prescriptionDetailsList[index].days}", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + ], + ), + SizedBox(height: 8.h), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.prescription_remarks_icon, width: 18.h, height: 18.h), + SizedBox(width: 9.h), + Expanded(child: "${LocaleKeys.remarks.tr(context: context)}: ${prescriptionVM.prescriptionDetailsList[index].remarks!}".toText10(isBold: true)), + ], + ) + ], + ), + ), + ) + ], + ), + ), + ), + ), + ); + }, + ).paddingSymmetrical(24.h, 0.h), + ], + ); + }), + ), + ); + } +} diff --git a/lib/presentation/prescriptions/prescriptions_list_page.dart b/lib/presentation/prescriptions/prescriptions_list_page.dart index c678a8e..4aa1a1b 100644 --- a/lib/presentation/prescriptions/prescriptions_list_page.dart +++ b/lib/presentation/prescriptions/prescriptions_list_page.dart @@ -11,9 +11,11 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; +import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; class PrescriptionsListPage extends StatefulWidget { @@ -45,248 +47,252 @@ class _PrescriptionsListPageState extends State { title: LocaleKeys.prescriptions.tr(context: context).toText18(), backgroundColor: AppColors.bgScaffoldColor, ), - body: Padding( - padding: EdgeInsets.all(24.h), - child: SingleChildScrollView( - child: Consumer(builder: (context, model, child) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.prescriptions.tr(context: context).toText24(isBold: true), - SizedBox(height: 16.h), - // Build Tab Bar - SizedBox(height: 16.h), - // Clinic & Hospital Sort - Row( - children: [ - CustomButton( - text: LocaleKeys.byClinic.tr(context: context), - onPressed: () { - model.setIsSortByClinic(true); - }, - backgroundColor: model.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, - borderColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), - textColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, - fontSize: 12, - fontWeight: FontWeight.w500, - borderRadius: 10, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - ), - SizedBox(width: 8.h), - CustomButton( - text: LocaleKeys.byHospital.tr(context: context), - onPressed: () { - model.setIsSortByClinic(false); - }, - backgroundColor: model.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, - borderColor: model.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor, - textColor: model.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, - fontSize: 12, - fontWeight: FontWeight.w500, - borderRadius: 10, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - ), - ], - ), - SizedBox(height: 20.h), - // Expandable list - ListView.builder( - itemCount: model.isPrescriptionsOrdersLoading ? 4 : model.patientPrescriptionOrdersViewList.length, - physics: NeverScrollableScrollPhysics(), - shrinkWrap: true, - padding: const EdgeInsets.only(left: 0, right: 8), - itemBuilder: (context, index) { - final isExpanded = expandedIndex == index; - return model.isPrescriptionsOrdersLoading - ? const MoviesShimmerWidget() - : AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 500), - child: SlideAnimation( - verticalOffset: 100.0, - child: FadeInAnimation( - child: AnimatedContainer( - duration: Duration(milliseconds: 300), - curve: Curves.easeInOut, - margin: EdgeInsets.symmetric(vertical: 8.h), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), - child: InkWell( - onTap: () { - setState(() { - expandedIndex = isExpanded ? null : index; - }); - }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.all(16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - CustomButton( - text: "${model.patientPrescriptionOrdersViewList[index].prescriptionsList!.length} Prescriptions Available", - onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 30.h, - ), - Icon(isExpanded ? Icons.expand_less : Icons.expand_more), - ], - ), - SizedBox(height: 8.h), - model.patientPrescriptionOrdersViewList[index].filterName!.toText16(isBold: true) - ], - ), + body: SingleChildScrollView( + child: Consumer(builder: (context, model, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.prescriptions.tr(context: context).toText24(isBold: true).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 16.h), + // Build Tab Bar + SizedBox(height: 16.h), + // Clinic & Hospital Sort + Row( + children: [ + CustomButton( + text: LocaleKeys.byClinic.tr(context: context), + onPressed: () { + model.setIsSortByClinic(true); + }, + backgroundColor: model.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, + borderColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), + textColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 10, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), + SizedBox(width: 8.h), + CustomButton( + text: LocaleKeys.byHospital.tr(context: context), + onPressed: () { + model.setIsSortByClinic(false); + }, + backgroundColor: model.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, + borderColor: model.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor, + textColor: model.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 10, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), + ], + ).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 20.h), + // Expandable list + ListView.builder( + itemCount: model.isPrescriptionsOrdersLoading ? 4 : model.patientPrescriptionOrdersViewList.length, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: const EdgeInsets.only(left: 0, right: 8), + itemBuilder: (context, index) { + final isExpanded = expandedIndex == index; + return model.isPrescriptionsOrdersLoading + ? const MoviesShimmerWidget() + : AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: AnimatedContainer( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + margin: EdgeInsets.symmetric(vertical: 8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), + child: InkWell( + onTap: () { + setState(() { + expandedIndex = isExpanded ? null : index; + }); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + CustomButton( + text: "${model.patientPrescriptionOrdersViewList[index].prescriptionsList!.length} Prescriptions Available", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 10, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + Icon(isExpanded ? Icons.expand_less : Icons.expand_more), + ], + ), + SizedBox(height: 8.h), + model.patientPrescriptionOrdersViewList[index].filterName!.toText16(isBold: true) + ], ), - AnimatedSwitcher( - duration: Duration(milliseconds: 500), - switchInCurve: Curves.easeIn, - switchOutCurve: Curves.easeOut, - transitionBuilder: (Widget child, Animation animation) { - return FadeTransition( - opacity: animation, - child: SizeTransition( - sizeFactor: animation, - axisAlignment: 0.0, - child: child, - ), - ); - }, - child: isExpanded - ? Container( - key: ValueKey(index), - padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ...model.patientPrescriptionOrdersViewList[index].prescriptionsList!.map((prescription) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Image.network( - prescription.doctorImageURL!, - width: 24.h, - height: 24.h, - fit: BoxFit.fill, - ).circle(100), - SizedBox(width: 8.h), - Expanded(child: prescription.doctorName!.toText14(weight: FontWeight.w500)), - ], - ), - SizedBox(height: 8.h), - Row( - children: [ - CustomButton( - text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(prescription.appointmentDate), false), + ), + AnimatedSwitcher( + duration: Duration(milliseconds: 500), + switchInCurve: Curves.easeIn, + switchOutCurve: Curves.easeOut, + transitionBuilder: (Widget child, Animation animation) { + return FadeTransition( + opacity: animation, + child: SizeTransition( + sizeFactor: animation, + axisAlignment: 0.0, + child: child, + ), + ); + }, + child: isExpanded + ? Container( + key: ValueKey(index), + padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...model.patientPrescriptionOrdersViewList[index].prescriptionsList!.map((prescription) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.network( + prescription.doctorImageURL!, + width: 24.h, + height: 24.h, + fit: BoxFit.fill, + ).circle(100), + SizedBox(width: 8.h), + Expanded(child: prescription.doctorName!.toText14(weight: FontWeight.w500)), + ], + ), + SizedBox(height: 8.h), + Row( + children: [ + CustomButton( + text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(prescription.appointmentDate), false), + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 24.h, + ), + SizedBox(width: 8.h), + CustomButton( + text: model.isSortByClinic ? prescription.name! : prescription.clinicDescription!, + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 24.h, + ), + ], + ), + SizedBox(height: 8.h), + Row( + children: [ + Expanded( + flex: 6, + child: CustomButton( + text: prescription.isHomeMedicineDeliverySupported! + ? LocaleKeys.resendOrder.tr(context: context) + : LocaleKeys.prescriptionDeliveryError.tr(context: context), onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 12, + backgroundColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor.withOpacity(0.15) : AppColors.greyF7Color, + borderColor: AppColors.successColor.withOpacity(0.01), + textColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35), + fontSize: prescription.isHomeMedicineDeliverySupported! ? 14 : 12, fontWeight: FontWeight.w500, - borderRadius: 8, + borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 24.h, + height: 40.h, + icon: AppAssets.prescription_refill_icon, + iconColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35), + iconSize: 14.h, ), - SizedBox(width: 8.h), - CustomButton( - text: model.isSortByClinic ? prescription.name! : prescription.clinicDescription!, - onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 12, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 24.h, - ), - ], - ), - SizedBox(height: 8.h), - Row( - children: [ - Expanded( - flex: 6, - child: CustomButton( - text: prescription.isHomeMedicineDeliverySupported! - ? LocaleKeys.resendOrder.tr(context: context) - : LocaleKeys.prescriptionDeliveryError.tr(context: context), - onPressed: () {}, - backgroundColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor.withOpacity(0.15) : AppColors.greyF7Color, - borderColor: AppColors.successColor.withOpacity(0.01), - textColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35), - fontSize: prescription.isHomeMedicineDeliverySupported! ? 14 : 12, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - icon: AppAssets.prescription_refill_icon, - iconColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35), - iconSize: 14.h, + ), + SizedBox(width: 8.h), + Expanded( + flex: 1, + child: Container( + height: 40.h, + width: 40.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.textColor, + borderRadius: 10.h, ), - ), - SizedBox(width: 8.h), - Expanded( - flex: 1, - child: Container( - height: 40.h, - width: 40.h, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.textColor, - borderRadius: 10.h, - ), - child: Padding( - padding: EdgeInsets.all(8.h), - child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, - width: 10.h, - height: 10.h, - fit: BoxFit.contain, - ), + child: Padding( + padding: EdgeInsets.all(8.h), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, + width: 10.h, + height: 10.h, + fit: BoxFit.contain, ), ), - ), - ], - ), - SizedBox(height: 12.h), - Divider(color: AppColors.borderOnlyColor.withOpacity(0.05), height: 1.h), - SizedBox(height: 12.h), - ], - ); - }).toList(), - ], - ), - ) - : SizedBox.shrink(), - ), - ], - ), + ).onPress(() { + model.setPrescriptionsDetailsLoading(); + Navigator.of(context).push( + FadePage( + page: PrescriptionDetailPage(prescriptionsResponseModel: prescription), + ), + ); + }), + ), + ], + ), + SizedBox(height: 12.h), + Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h), + SizedBox(height: 12.h), + ], + ); + }).toList(), + ], + ), + ) + : SizedBox.shrink(), + ), + ], ), ), ), ), - ); - }, - ), - ], - ); - }), - ), + ), + ); + }, + ).paddingSymmetrical(24.h, 0.h), + ], + ); + }), ), ); } diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index d9cf167..018727c 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -32,6 +32,7 @@ class AppColors { static const Color borderOnlyColor = Color(0xFF2E3039); static const Color dividerColor = Color(0xFFD2D2D2); static const Color warningColorYellow = Color(0xFFF4A308); + static const Color ratingColorYellow = Color(0xFFFFAF15); //Chips static const Color successColor = Color(0xff18C273);