diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 6c4ac3d..328e8fc 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -19,5 +19,5 @@
Geofence requests happened too frequently.
- sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg
+
diff --git a/assets/images/svg/E_Referral.svg b/assets/images/svg/E_Referral.svg
new file mode 100644
index 0000000..fb6b859
--- /dev/null
+++ b/assets/images/svg/E_Referral.svg
@@ -0,0 +1,9 @@
+
diff --git a/assets/images/svg/file.svg b/assets/images/svg/file.svg
new file mode 100644
index 0000000..004145c
--- /dev/null
+++ b/assets/images/svg/file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/images/svg/files.svg b/assets/images/svg/files.svg
new file mode 100644
index 0000000..dfc862e
--- /dev/null
+++ b/assets/images/svg/files.svg
@@ -0,0 +1,14 @@
+
diff --git a/assets/images/svg/gallery.svg b/assets/images/svg/gallery.svg
new file mode 100644
index 0000000..ec1c45e
--- /dev/null
+++ b/assets/images/svg/gallery.svg
@@ -0,0 +1,11 @@
+
diff --git a/assets/images/svg/open_camera.svg b/assets/images/svg/open_camera.svg
new file mode 100644
index 0000000..171452a
--- /dev/null
+++ b/assets/images/svg/open_camera.svg
@@ -0,0 +1,7 @@
+
diff --git a/assets/images/svg/rate_1.svg b/assets/images/svg/rate_1.svg
new file mode 100644
index 0000000..8e1c2f2
--- /dev/null
+++ b/assets/images/svg/rate_1.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/rate_2.svg b/assets/images/svg/rate_2.svg
new file mode 100644
index 0000000..9500556
--- /dev/null
+++ b/assets/images/svg/rate_2.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/rate_3.svg b/assets/images/svg/rate_3.svg
new file mode 100644
index 0000000..7c9d0f5
--- /dev/null
+++ b/assets/images/svg/rate_3.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/rate_4.svg b/assets/images/svg/rate_4.svg
new file mode 100644
index 0000000..7f82267
--- /dev/null
+++ b/assets/images/svg/rate_4.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/rate_5.svg b/assets/images/svg/rate_5.svg
new file mode 100644
index 0000000..a7308ba
--- /dev/null
+++ b/assets/images/svg/rate_5.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart
index 9bab873..a3cf213 100644
--- a/lib/core/api_consts.dart
+++ b/lib/core/api_consts.dart
@@ -14,8 +14,8 @@ var PACKAGES_ORDERS = '/api/orders';
var PACKAGES_ORDER_HISTORY = '/api/orders/items';
var PACKAGES_TAMARA_OPT = '/api/orders/paymentoptions/tamara';
// var BASE_URL = 'http://10.50.100.198:2018/';
-var BASE_URL = 'https://uat.hmgwebservices.com/';
-// var BASE_URL = 'https://hmgwebservices.com/';
+// var BASE_URL = 'https://uat.hmgwebservices.com/';
+var BASE_URL = 'https://hmgwebservices.com/';
// var BASE_URL = 'http://10.201.204.103/';
// var BASE_URL = 'https://orash.cloudsolutions.com.sa/';
// var BASE_URL = 'https://vidauat.cloudsolutions.com.sa/';
@@ -700,7 +700,7 @@ var GET_PRESCRIPTION_INSTRUCTIONS_PDF = 'Services/ChatBot_Service.svc/REST/Chatb
class ApiConsts {
static const maxSmallScreen = 660;
- static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat;
+ static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT
diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart
index c8659a7..1bb1c5a 100644
--- a/lib/core/app_state.dart
+++ b/lib/core/app_state.dart
@@ -45,7 +45,7 @@ class AppState {
bool isChildLoggedIn = false;
bool isGMSAvailable = true;
bool isAndroid = true;
-
+ bool isRatedVisible =false;
void setAuthenticatedUser(AuthenticatedUser? authenticatedUser, {bool isFamily = false}) {
if (isFamily) {
_authenticatedChildUser = authenticatedUser;
@@ -172,4 +172,8 @@ class AppState {
userLong = 0.0;
userLong = 0.0;
}
+
+ setRatedVisible(bool value) {
+ isRatedVisible = value;
+ }
}
diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart
index cd4065e..118c54a0 100644
--- a/lib/features/lab/lab_view_model.dart
+++ b/lib/features/lab/lab_view_model.dart
@@ -1,6 +1,5 @@
import 'dart:collection';
import 'dart:core';
-import 'dart:math';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
@@ -18,7 +17,6 @@ import 'package:hmg_patient_app_new/theme/colors.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:intl/intl.dart' show DateFormat;
-import 'package:logger/logger.dart';
class LabViewModel extends ChangeNotifier {
bool isLabOrdersLoading = false;
@@ -82,6 +80,12 @@ class LabViewModel extends ChangeNotifier {
double minY = double.infinity;
double maxX = double.infinity;
+ // --- New grouping fields ---
+ bool isSortByClinic = true;
+ List> patientLabOrdersByClinic = [];
+ List> patientLabOrdersByHospital = [];
+ List> patientLabOrdersViewList = [];
+
LabViewModel(
{required this.labRepo,
required this.errorHandlerService,
@@ -94,6 +98,9 @@ class LabViewModel extends ChangeNotifier {
labOrderTests.clear();
isLabOrdersLoading = true;
isLabResultsLoading = true;
+ patientLabOrdersByClinic.clear();
+ patientLabOrdersByHospital.clear();
+ patientLabOrdersViewList.clear();
getPatientLabOrders();
// }
notifyListeners();
@@ -104,6 +111,12 @@ class LabViewModel extends ChangeNotifier {
notifyListeners();
}
+ void setIsSortByClinic(bool value) {
+ isSortByClinic = value;
+ patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
+ notifyListeners();
+ }
+
Future getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
// if (!isLabNeedToLoad) return;
@@ -112,6 +125,9 @@ class LabViewModel extends ChangeNotifier {
uniqueTests.clear();
labOrderTests.clear();
uniqueTests = {};
+ patientLabOrdersByClinic.clear();
+ patientLabOrdersByHospital.clear();
+ patientLabOrdersViewList.clear();
notifyListeners();
final result = await labRepo.getPatientLabOrders();
@@ -122,7 +138,7 @@ class LabViewModel extends ChangeNotifier {
isLabResultsLoading = false;
notifyListeners();
},
- // => await errorHandlerService.handleError(failure: failure),
+ // => await errorHandler_service.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
@@ -132,6 +148,23 @@ class LabViewModel extends ChangeNotifier {
tempLabOrdersList = apiResponse.data!;
isLabOrdersLoading = false;
isLabResultsLoading = false;
+
+ // --- Build groups by clinic and by hospital (projectName) ---
+ final clinicMap = >{};
+ final hospitalMap = >{};
+
+ for (var order in patientLabOrders) {
+ final clinicKey = (order.clinicDescription ?? 'Unknown').trim();
+ clinicMap.putIfAbsent(clinicKey, () => []).add(order);
+
+ final hospitalKey = (order.projectName ?? order.projectID ?? 'Unknown').toString().trim();
+ hospitalMap.putIfAbsent(hospitalKey, () => []).add(order);
+ }
+
+ patientLabOrdersByClinic = clinicMap.values.toList();
+ patientLabOrdersByHospital = hospitalMap.values.toList();
+ patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
+
filterSuggestions();
getUniqueTestDescription();
isLabNeedToLoad = false;
@@ -206,7 +239,6 @@ class LabViewModel extends ChangeNotifier {
createdOn: item.createdOn,
model: item))
};
- var sortedResult = SplayTreeSet.from(uniqueTests, (a, b) => a.description?[0].toUpperCase().compareTo(b.description?[0] ?? "") ?? -1);
uniqueTestsList = uniqueTests.toList();
uniqueTestsList.sort((a, b) {
return a.description!.toLowerCase().compareTo(b.description!.toLowerCase());
@@ -291,7 +323,7 @@ class LabViewModel extends ChangeNotifier {
PatientLabOrdersResponseModel laborder) async {
isLabResultByHospitalLoading = true;
notifyListeners();
- mainLabResultsByHospitals.clear;
+ mainLabResultsByHospitals.clear();
final result = await labRepo.getPatientLabResultsByHospitals(laborder,
Utils.isVidaPlusProject(int.parse(laborder.projectID ?? "0")));
@@ -334,8 +366,7 @@ class LabViewModel extends ChangeNotifier {
LoaderBottomSheet.hideLoader();
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
- var sortedResponse = sortByFlagAndValue(apiResponse.data!);
- var recentThree = sort(sortedResponse);
+ var recentThree = sort(apiResponse.data!);
mainLabResults = recentThree;
@@ -617,19 +648,6 @@ class LabViewModel extends ChangeNotifier {
}
List sort(List original) {
- DateTime? parseVerifiedDate(String? raw) {
- if (raw == null) return null;
- final regex = RegExp(r'\/Date\((\d+)\)\/');
- final match = regex.firstMatch(raw);
- if (match != null) {
- final millis = int.tryParse(match.group(1)!);
- if (millis != null) {
-
- return DateTime.fromMillisecondsSinceEpoch(millis);
- }
- }
- return null;
- }
final copy = List.from(original);
copy.sort((a, b) {
@@ -649,8 +667,8 @@ class LabViewModel extends ChangeNotifier {
final copy = List.from(original);
copy.sort((a, b) {
- final aDate =a.time;
- final bDate = a.time;
+ final aDate = a.time;
+ final bDate = b.time;
final now = DateTime.now();
if (aDate == now && bDate == now) return 0;
if (aDate == now) return 1;
diff --git a/lib/features/medical_file/medical_file_view_model.dart b/lib/features/medical_file/medical_file_view_model.dart
index de7f067..68e83cd 100644
--- a/lib/features/medical_file/medical_file_view_model.dart
+++ b/lib/features/medical_file/medical_file_view_model.dart
@@ -36,6 +36,12 @@ class MedicalFileViewModel extends ChangeNotifier {
List patientVaccineList = [];
List patientSickLeaveList = [];
+ List patientSickLeavesByClinic = [];
+ List patientSickLeavesByHospital = [];
+ List patientSickLeavesViewList = [];
+
+ bool isSickLeavesSortByClinic = true;
+
List patientAllergiesList = [];
List patientMedicalReportList = [];
@@ -44,6 +50,12 @@ class MedicalFileViewModel extends ChangeNotifier {
List patientMedicalReportReadyList = [];
List patientMedicalReportCancelledList = [];
+ List patientMedicalReportsByClinic = [];
+ List patientMedicalReportsByHospital = [];
+ List patientMedicalReportsViewList = [];
+
+ bool isMedicalReportsSortByClinic = true;
+
List patientMedicalReportAppointmentHistoryList = [];
PatientAppointmentHistoryResponseModel? patientMedicalReportSelectedAppointment;
@@ -90,6 +102,49 @@ class MedicalFileViewModel extends ChangeNotifier {
} else if (index == 2) {
patientMedicalReportList = patientMedicalReportCancelledList;
}
+ // Re-group by clinic/hospital for the selected tab
+ _groupMedicalReportsByClinicAndHospital();
+ notifyListeners();
+ }
+
+ void _groupMedicalReportsByClinicAndHospital() {
+ patientMedicalReportsByClinic.clear();
+ patientMedicalReportsByHospital.clear();
+
+ for (var element in patientMedicalReportList) {
+ // Group by clinic
+ List reportsByClinic = patientMedicalReportsByClinic.where((elementClinic) => elementClinic.filterName == element.clinicDescription).toList();
+
+ if (reportsByClinic.isNotEmpty) {
+ patientMedicalReportsByClinic[patientMedicalReportsByClinic.indexOf(reportsByClinic[0])].medicalReportsList!.add(element);
+ } else {
+ patientMedicalReportsByClinic.add(MedicalReportList(filterName: element.clinicDescription, medicalReport: element));
+ }
+
+ // Group by hospital
+ List reportsByHospital = patientMedicalReportsByHospital.where((elementHospital) => elementHospital.filterName == element.projectName).toList();
+
+ if (reportsByHospital.isNotEmpty) {
+ patientMedicalReportsByHospital[patientMedicalReportsByHospital.indexOf(reportsByHospital[0])].medicalReportsList!.add(element);
+ } else {
+ patientMedicalReportsByHospital.add(MedicalReportList(filterName: element.projectName, medicalReport: element));
+ }
+ }
+
+ if (isMedicalReportsSortByClinic) {
+ patientMedicalReportsViewList = patientMedicalReportsByClinic;
+ } else {
+ patientMedicalReportsViewList = patientMedicalReportsByHospital;
+ }
+ }
+
+ setIsMedicalReportsSortByClinic(bool value) {
+ isMedicalReportsSortByClinic = value;
+ if (isMedicalReportsSortByClinic) {
+ patientMedicalReportsViewList = patientMedicalReportsByClinic;
+ } else {
+ patientMedicalReportsViewList = patientMedicalReportsByHospital;
+ }
notifyListeners();
}
@@ -110,17 +165,35 @@ class MedicalFileViewModel extends ChangeNotifier {
setIsPatientSickLeaveListLoading(bool val) {
if (val) {
patientSickLeaveList.clear();
+ patientSickLeavesByClinic.clear();
+ patientSickLeavesByHospital.clear();
+ patientSickLeavesViewList.clear();
patientSickLeavePDFBase64 = "";
+ isSickLeavesSortByClinic = true;
}
isPatientSickLeaveListLoading = val;
notifyListeners();
}
+ setIsSickLeavesSortByClinic(bool value) {
+ isSickLeavesSortByClinic = value;
+ if (isSickLeavesSortByClinic) {
+ patientSickLeavesViewList = patientSickLeavesByClinic;
+ } else {
+ patientSickLeavesViewList = patientSickLeavesByHospital;
+ }
+ notifyListeners();
+ }
+
setIsPatientMedicalReportsLoading(bool val) {
if (val) {
onMedicalReportTabChange(0);
patientMedicalReportList.clear();
+ patientMedicalReportsByClinic.clear();
+ patientMedicalReportsByHospital.clear();
+ patientMedicalReportsViewList.clear();
patientMedicalReportPDFBase64 = "";
+ isMedicalReportsSortByClinic = true;
}
isPatientMedicalReportsListLoading = val;
notifyListeners();
@@ -214,6 +287,29 @@ class MedicalFileViewModel extends ChangeNotifier {
} else if (apiResponse.messageStatus == 1) {
patientSickLeaveList = apiResponse.data!;
isPatientSickLeaveListLoading = false;
+
+ // Group by clinic and hospital
+ for (var element in patientSickLeaveList) {
+ // Group by clinic
+ List sickLeavesByClinic = patientSickLeavesByClinic.where((elementClinic) => elementClinic.filterName == element.clinicName).toList();
+
+ if (sickLeavesByClinic.isNotEmpty) {
+ patientSickLeavesByClinic[patientSickLeavesByClinic.indexOf(sickLeavesByClinic[0])].sickLeavesList!.add(element);
+ } else {
+ patientSickLeavesByClinic.add(SickLeaveList(filterName: element.clinicName, sickLeaves: element));
+ }
+
+ // Group by hospital
+ List sickLeavesByHospital = patientSickLeavesByHospital.where((elementHospital) => elementHospital.filterName == element.projectName).toList();
+
+ if (sickLeavesByHospital.isNotEmpty) {
+ patientSickLeavesByHospital[patientSickLeavesByHospital.indexOf(sickLeavesByHospital[0])].sickLeavesList!.add(element);
+ } else {
+ patientSickLeavesByHospital.add(SickLeaveList(filterName: element.projectName, sickLeaves: element));
+ }
+ }
+ patientSickLeavesViewList = patientSickLeavesByClinic;
+
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
diff --git a/lib/features/medical_file/models/patient_medical_response_model.dart b/lib/features/medical_file/models/patient_medical_response_model.dart
index 52785af..3bcf5cd 100644
--- a/lib/features/medical_file/models/patient_medical_response_model.dart
+++ b/lib/features/medical_file/models/patient_medical_response_model.dart
@@ -190,3 +190,16 @@ class PatientMedicalReportResponseModel {
return data;
}
}
+
+class MedicalReportList {
+ String? filterName;
+ List? medicalReportsList;
+
+ MedicalReportList({this.filterName, PatientMedicalReportResponseModel? medicalReport}) {
+ medicalReportsList = [];
+ if (medicalReport != null) {
+ medicalReportsList!.add(medicalReport);
+ }
+ }
+}
+
diff --git a/lib/features/medical_file/models/patient_sickleave_response_model.dart b/lib/features/medical_file/models/patient_sickleave_response_model.dart
index 3bf732c..53c886f 100644
--- a/lib/features/medical_file/models/patient_sickleave_response_model.dart
+++ b/lib/features/medical_file/models/patient_sickleave_response_model.dart
@@ -174,3 +174,16 @@ class PatientSickLeavesResponseModel {
return data;
}
}
+
+class SickLeaveList {
+ String? filterName;
+ List? sickLeavesList;
+
+ SickLeaveList({this.filterName, PatientSickLeavesResponseModel? sickLeaves}) {
+ sickLeavesList = [];
+ if (sickLeaves != null) {
+ sickLeavesList!.add(sickLeaves);
+ }
+ }
+}
+
diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart
index d077970..b4a25ee 100644
--- a/lib/features/my_appointments/my_appointments_view_model.dart
+++ b/lib/features/my_appointments/my_appointments_view_model.dart
@@ -60,6 +60,12 @@ class MyAppointmentsViewModel extends ChangeNotifier {
List patientEyeMeasurementsAppointmentsHistoryList = [];
+ // Grouping by Clinic and Hospital
+ List patientAppointmentsByClinic = [];
+ List patientAppointmentsByHospital = [];
+ List patientAppointmentsViewList = [];
+ bool isAppointmentsSortByClinic = true;
+
List askDoctorRequestTypeList = [];
PatientAppointmentShareResponseModel? patientAppointmentShareResponseModel;
@@ -82,6 +88,43 @@ class MyAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
+ setIsAppointmentsSortByClinic(bool value) {
+ isAppointmentsSortByClinic = value;
+ _groupAppointmentsByClinicAndHospital();
+ notifyListeners();
+ }
+
+ void _groupAppointmentsByClinicAndHospital() {
+ patientAppointmentsByClinic.clear();
+ patientAppointmentsByHospital.clear();
+
+ for (var element in filteredAppointmentList) {
+ // Group by clinic
+ List appointmentsByClinic = patientAppointmentsByClinic.where((elementClinic) => elementClinic.filterName == element.clinicName).toList();
+
+ if (appointmentsByClinic.isNotEmpty) {
+ patientAppointmentsByClinic[patientAppointmentsByClinic.indexOf(appointmentsByClinic[0])].patientDoctorAppointmentList!.add(element);
+ } else {
+ patientAppointmentsByClinic.add(PatientAppointmentList(filterName: element.clinicName, patientDoctorAppointment: element));
+ }
+
+ // Group by hospital
+ List appointmentsByHospital = patientAppointmentsByHospital.where((elementHospital) => elementHospital.filterName == element.projectName).toList();
+
+ if (appointmentsByHospital.isNotEmpty) {
+ patientAppointmentsByHospital[patientAppointmentsByHospital.indexOf(appointmentsByHospital[0])].patientDoctorAppointmentList!.add(element);
+ } else {
+ patientAppointmentsByHospital.add(PatientAppointmentList(filterName: element.projectName, patientDoctorAppointment: element));
+ }
+ }
+
+ if (isAppointmentsSortByClinic) {
+ patientAppointmentsViewList = patientAppointmentsByClinic;
+ } else {
+ patientAppointmentsViewList = patientAppointmentsByHospital;
+ }
+ }
+
initAppointmentsViewModel() {
if (isAppointmentDataToBeLoaded) {
patientAppointmentsHistoryList.clear();
@@ -513,6 +556,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
break;
}
getFiltersForSelectedAppointmentList(filteredAppointmentList);
+ _groupAppointmentsByClinicAndHospital();
notifyListeners();
}
@@ -570,6 +614,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
} catch (e) {}
}
}
+ _groupAppointmentsByClinicAndHospital();
notifyListeners();
}
diff --git a/lib/features/radiology/radiology_view_model.dart b/lib/features/radiology/radiology_view_model.dart
index 4a87906..986945e 100644
--- a/lib/features/radiology/radiology_view_model.dart
+++ b/lib/features/radiology/radiology_view_model.dart
@@ -29,6 +29,18 @@ class RadiologyViewModel extends ChangeNotifier {
late PatientRadiologyResponseModel patientRadiologyOrderByAppointment;
+ // --- Grouping fields: By Clinic / By Hospital ---
+ bool isSortByClinic = true;
+ List> patientRadiologyOrdersByClinic = [];
+ List> patientRadiologyOrdersByHospital = [];
+ List> patientRadiologyOrdersViewList = [];
+
+ void setIsSortByClinic(bool value) {
+ isSortByClinic = value;
+ patientRadiologyOrdersViewList = isSortByClinic ? patientRadiologyOrdersByClinic : patientRadiologyOrdersByHospital;
+ notifyListeners();
+ }
+
RadiologyViewModel({required this.radiologyRepo, required this.errorHandlerService, required this.navigationService});
initRadiologyViewModel() {
diff --git a/lib/presentation/appointments/my_appointments_page.dart b/lib/presentation/appointments/my_appointments_page.dart
index 934aca4..abf2464 100644
--- a/lib/presentation/appointments/my_appointments_page.dart
+++ b/lib/presentation/appointments/my_appointments_page.dart
@@ -4,6 +4,9 @@ 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/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.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';
@@ -35,6 +38,7 @@ class MyAppointmentsPage extends StatefulWidget {
}
class _MyAppointmentsPageState extends State {
+ int? expandedIndex;
late MyAppointmentsViewModel myAppointmentsViewModel;
late BookAppointmentsViewModel bookAppointmentsViewModel;
@@ -68,6 +72,9 @@ class _MyAppointmentsPageState extends State {
CustomTabBarModel(null, "Completed".needTranslation),
],
onTabChange: (index) {
+ setState(() {
+ expandedIndex = null;
+ });
myAppointmentsViewModel.onTabChange(index);
myAppointmentsViewModel.updateListWRTTab(index);
context.read().flush();
@@ -91,17 +98,55 @@ class _MyAppointmentsPageState extends State {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
+ SizedBox(height: 16.h),
+ // Clinic & Hospital Sort
+ Row(
+ children: [
+ CustomButton(
+ text: LocaleKeys.byClinic.tr(context: context),
+ onPressed: () {
+ myAppointmentsVM.setIsAppointmentsSortByClinic(true);
+ },
+ backgroundColor: myAppointmentsVM.isAppointmentsSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
+ borderColor: myAppointmentsVM.isAppointmentsSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
+ textColor: myAppointmentsVM.isAppointmentsSortByClinic ? 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: () {
+ myAppointmentsVM.setIsAppointmentsSortByClinic(false);
+ },
+ backgroundColor: myAppointmentsVM.isAppointmentsSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
+ borderColor: myAppointmentsVM.isAppointmentsSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor,
+ textColor: myAppointmentsVM.isAppointmentsSortByClinic ? 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),
Visibility(visible: myAppointmentsVM.availableFilters.isNotEmpty, child: getAppointmentFilters(myAppointmentsVM)),
- ListView.separated(
- padding: EdgeInsets.only(top: 16.h),
+ SizedBox(height: 8.h),
+ // Expandable list
+ ListView.builder(
+ padding: EdgeInsets.only(top: 8.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: myAppointmentsVM.isMyAppointmentsLoading
- ? 5
+ ? 4
: filteredAppointmentList.isNotEmpty
- ? filteredAppointmentList.length
+ ? myAppointmentsVM.patientAppointmentsViewList.length
: 1,
itemBuilder: (context, index) {
+ final isExpanded = expandedIndex == index;
return myAppointmentsVM.isMyAppointmentsLoading
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
@@ -123,15 +168,149 @@ class _MyAppointmentsPageState extends State {
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
- child: AppointmentCard(
- patientAppointmentHistoryResponseModel: filteredAppointmentList[index],
- myAppointmentsViewModel: myAppointmentsViewModel,
- bookAppointmentsViewModel: bookAppointmentsViewModel,
- isLoading: false,
- isFromHomePage: false,
+ 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: "${myAppointmentsVM.patientAppointmentsViewList[index].patientDoctorAppointmentList!.length} Appointments",
+ 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),
+ myAppointmentsVM.patientAppointmentsViewList[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: [
+ ...myAppointmentsVM.patientAppointmentsViewList[index].patientDoctorAppointmentList!.map((appointment) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Image.network(
+ appointment.doctorImageURL!,
+ width: 24.h,
+ height: 24.h,
+ fit: BoxFit.fill,
+ ).circle(100),
+ SizedBox(width: 8.h),
+ Expanded(child: appointment.doctorNameObj!.toText14(weight: FontWeight.w500)),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ Row(
+ children: [
+ CustomButton(
+ text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(appointment.appointmentDate), false),
+ 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: 24.h,
+ ),
+ SizedBox(width: 8.h),
+ CustomButton(
+ text: myAppointmentsVM.isAppointmentsSortByClinic ? appointment.projectName! : appointment.clinicName!,
+ 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: 24.h,
+ ),
+ SizedBox(width: 8.h),
+ CustomButton(
+ text: appointment.statusDesc ?? "",
+ 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: 24.h,
+ ),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ AppointmentCard(
+ patientAppointmentHistoryResponseModel: appointment,
+ myAppointmentsViewModel: myAppointmentsViewModel,
+ bookAppointmentsViewModel: bookAppointmentsViewModel,
+ isLoading: false,
+ isFromHomePage: false,
+ ),
+ SizedBox(height: 12.h),
+ Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h),
+ SizedBox(height: 12.h),
+ ],
+ );
+ }),
+ ],
+ ),
+ )
+ : SizedBox.shrink(),
+ ),
+ ],
+ ),
),
- ).paddingSymmetrical(24.h, 0.h),
+ ),
),
),
)
@@ -160,8 +339,7 @@ class _MyAppointmentsPageState extends State {
).paddingSymmetrical(48.h, 0.h),
);
},
- separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
- ),
+ ).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 24.h),
],
);
diff --git a/lib/presentation/appointments/my_doctors_page.dart b/lib/presentation/appointments/my_doctors_page.dart
index 51e9703..3740a4a 100644
--- a/lib/presentation/appointments/my_doctors_page.dart
+++ b/lib/presentation/appointments/my_doctors_page.dart
@@ -1,6 +1,5 @@
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/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
@@ -21,169 +20,377 @@ import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import '../../widgets/chip/app_custom_chip_widget.dart';
+import '../../widgets/buttons/custom_button.dart';
-class MyDoctorsPage extends StatelessWidget {
+class MyDoctorsPage extends StatefulWidget {
MyDoctorsPage({super.key});
+ @override
+ State createState() => _MyDoctorsPageState();
+}
+
+class _MyDoctorsPageState extends State {
late MyAppointmentsViewModel myAppointmentsViewModel;
late BookAppointmentsViewModel bookAppointmentsViewModel;
+ bool isSortByClinic = true; // default like lab page
+ int? expandedIndex;
+ final Map _groupKeys = {};
+
@override
Widget build(BuildContext context) {
- AppState appState = getIt.get();
myAppointmentsViewModel = Provider.of(context, listen: false);
bookAppointmentsViewModel = Provider.of(context, listen: false);
- return Scaffold(
- backgroundColor: AppColors.bgScaffoldColor,
- body: CollapsingListView(
+
+ return CollapsingListView(
title: LocaleKeys.myDoctor.tr(context: context),
- child: SingleChildScrollView(
- child: Consumer(builder: (context, myAppointmentsVM, child) {
+ child: Consumer(builder: (context, myAppointmentsVM, child) {
+ // build grouped lists from the flat list
+ final clinicMap = >{};
+ final hospitalMap = >{};
+
+ for (var doc in myAppointmentsVM.patientMyDoctorsList) {
+ final clinicKey = (doc.clinicName ?? 'Unknown').trim();
+ clinicMap.putIfAbsent(clinicKey, () => []).add(doc);
+
+ final hospitalKey = (doc.projectName ?? doc.projectID?.toString() ?? 'Unknown').toString().trim();
+ hospitalMap.putIfAbsent(hospitalKey, () => []).add(doc);
+ }
+
+ final patientMyDoctorsByClinic = clinicMap.values.toList();
+ final patientMyDoctorsByHospital = hospitalMap.values.toList();
+ final patientMyDoctorsViewList = isSortByClinic ? patientMyDoctorsByClinic : patientMyDoctorsByHospital;
+
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
+ // Add Clinic / Hospital toggle buttons styled like LabOrdersPage
+ Padding(
+ padding: EdgeInsets.symmetric(horizontal: 24.h),
+ child: Row(
+ children: [
+ CustomButton(
+ text: LocaleKeys.byClinic.tr(context: context),
+ onPressed: () {
+ setState(() {
+ isSortByClinic = true;
+ expandedIndex = null;
+ });
+ },
+ backgroundColor: isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
+ borderColor: isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
+ textColor: 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: () {
+ setState(() {
+ isSortByClinic = false;
+ expandedIndex = null;
+ });
+ },
+ backgroundColor: isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
+ borderColor: isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor,
+ textColor: isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
+ fontSize: 12,
+ fontWeight: FontWeight.w500,
+ borderRadius: 10,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 40.h,
+
+ ),
+ ],
+ ),
+ ),
+ SizedBox(height: 16.h),
ListView.separated(
scrollDirection: Axis.vertical,
- itemCount: myAppointmentsVM.isPatientMyDoctorsLoading ? 5 : myAppointmentsVM.patientMyDoctorsList.length,
+ itemCount: myAppointmentsVM.isPatientMyDoctorsLoading ? 5 : (patientMyDoctorsViewList.isNotEmpty ? patientMyDoctorsViewList.length : 0),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.only(left: 24.h, right: 24.h),
itemBuilder: (context, index) {
- return myAppointmentsVM.isPatientMyDoctorsLoading
- ? Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 20.h,
- hasShadow: true,
- ),
- child: Padding(
- padding: EdgeInsets.all(14.h),
- child: Column(
+ if (myAppointmentsVM.isPatientMyDoctorsLoading) {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: true,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(14.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Image.network(
- "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
- width: 63.h,
- height: 63.h,
- fit: BoxFit.cover,
- ).circle(100).toShimmer2(isShow: true),
- SizedBox(width: 16.h),
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- "Dr John Smith".toText16(isBold: true).toShimmer2(isShow: true),
- SizedBox(height: 8.h),
- Wrap(
- direction: Axis.horizontal,
- spacing: 3.h,
- runSpacing: 4.h,
- children: [
- AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
- AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
- ],
- ),
- ],
- ),
- ),
- ],
- ),
- ],
- ),
- ),
- )
- : AnimationConfiguration.staggeredList(
- position: index,
- duration: const Duration(milliseconds: 1000),
- child: SlideAnimation(
- verticalOffset: 100.0,
- child: FadeInAnimation(
- child: Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 20.h,
- hasShadow: true,
- ),
- child: Padding(
- padding: EdgeInsets.all(14.h),
+ Image.network(
+ "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
+ width: 63.h,
+ height: 63.h,
+ fit: BoxFit.cover,
+ ).circle(100).toShimmer2(isShow: true),
+ SizedBox(width: 16.h),
+ Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Image.network(
- myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!,
- width: 63.h,
- height: 63.h,
- fit: BoxFit.cover,
- ).circle(100).toShimmer2(isShow: false),
- SizedBox(width: 16.h),
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- (myAppointmentsVM.patientMyDoctorsList[index].doctorName).toString().toText16(isBold: true).toShimmer2(isShow: false),
- SizedBox(height: 8.h),
- Wrap(
- direction: Axis.horizontal,
- spacing: 3.h,
- runSpacing: 4.h,
- children: [
- AppCustomChipWidget(labelText: myAppointmentsVM.patientMyDoctorsList[index].clinicName).toShimmer2(isShow: false, width: 16.h),
- AppCustomChipWidget(labelText: myAppointmentsVM.patientMyDoctorsList[index].projectName).toShimmer2(isShow: false, width: 16.h),
- ],
- ),
- ],
- ),
- ),
- ],
- ),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ "Dr John Smith".toText16(isBold: true).toShimmer2(isShow: true),
+ SizedBox(height: 8.h),
+ Wrap(
+ direction: Axis.horizontal,
+ spacing: 3.h,
+ runSpacing: 4.h,
children: [
- "".toText16(),
- Transform.flip(
- flipX: appState.isArabic(),
- child:
- Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon_small, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)),
+ AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
+ AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
],
),
],
),
),
- ).onPress(() async {
- bookAppointmentsViewModel.setSelectedDoctor(DoctorsListResponseModel(
- clinicID: myAppointmentsVM.patientMyDoctorsList[index].clinicID,
- projectID: myAppointmentsVM.patientMyDoctorsList[index].projectID,
- doctorID: myAppointmentsVM.patientMyDoctorsList[index].doctorID,
- ));
- LoaderBottomSheet.showLoader();
- await bookAppointmentsViewModel.getDoctorProfile(onSuccess: (dynamic respData) {
- LoaderBottomSheet.hideLoader();
- Navigator.of(context).push(
- CustomPageRoute(
- page: DoctorProfilePage(),
- ),
- );
- }, onError: (err) {
- LoaderBottomSheet.hideLoader();
- showCommonBottomSheetWithoutHeight(
- context,
- child: Utils.getErrorWidget(loadingText: err),
- callBackFunc: () {},
- isFullScreen: false,
- isCloseButtonVisible: true,
- );
- });
- }),
+ ],
),
+ ],
+ ),
+ ),
+ );
+ }
+
+ final group = patientMyDoctorsViewList[index];
+ final displayName = isSortByClinic ? (group.first.clinicName ?? 'Unknown') : (group.first.projectName ?? 'Unknown');
+ final isExpanded = expandedIndex == index;
+
+ return Container(
+ key: _groupKeys.putIfAbsent(index, () => GlobalKey()),
+ margin: EdgeInsets.only(bottom: 12.h),
+ padding: EdgeInsets.all(16.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: true,
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ GestureDetector(
+ onTap: () {
+ setState(() {
+ expandedIndex = isExpanded ? null : index;
+ });
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ final key = _groupKeys[index];
+ if (key != null && key.currentContext != null && expandedIndex == index) {
+ Scrollable.ensureVisible(
+ key.currentContext!,
+ duration: Duration(milliseconds: 350),
+ curve: Curves.easeInOut,
+ alignment: 0.1,
+ );
+ }
+ });
+ },
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ CustomButton(
+ text: "${group.length} ${'doctors'.needTranslation}",
+ onPressed: () {},
+ backgroundColor: AppColors.greyColor,
+ borderColor: AppColors.greyColor,
+ textColor: AppColors.blackColor,
+ fontSize: 10,
+ fontWeight: FontWeight.w500,
+ borderRadius: 8,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 30.h,
+ ),
+ Icon(isExpanded ? Icons.expand_less : Icons.chevron_right, color: AppColors.greyColor),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ Text(
+ displayName,
+ style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600),
+ overflow: TextOverflow.ellipsis,
+ ),
+ ],
),
- );
+ ),
+ AnimatedSwitcher(
+ duration: Duration(milliseconds: 400),
+ child: isExpanded
+ ? Container(
+ key: ValueKey(index),
+ padding: EdgeInsets.only(top: 12.h),
+ child: Column(
+ children: group.map((doctor) {
+ return Container(
+ margin: EdgeInsets.only(bottom: 12.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 12.h,
+ hasShadow: true,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(14.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Image.network(
+ (doctor?.doctorImageURL ?? doctor?.doctorImage ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png"),
+ width: 24.h,
+ height: 24.h,
+ fit: BoxFit.cover,
+ ).circle(100),
+ SizedBox(width: 8.h),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ (doctor?.doctorName ?? "").toString().toText14(weight: FontWeight.w500),
+ SizedBox(height: 6.h),
+ ],
+ ),
+ ),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ Row(
+ children: [
+ CustomButton(
+ text: isSortByClinic ? (doctor?.clinicName ?? "") : (doctor?.projectName ?? ""),
+ 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: 24.h,
+ ),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ Row(
+ children: [
+ Expanded(
+ flex: 6,
+ child: CustomButton(
+ text: "View Profile".needTranslation.tr(context: context),
+ onPressed: () async {
+ bookAppointmentsViewModel.setSelectedDoctor(DoctorsListResponseModel(
+ clinicID: doctor?.clinicID ?? 0,
+ projectID: doctor?.projectID ?? 0,
+ doctorID: doctor?.doctorID ?? 0,
+ ));
+ LoaderBottomSheet.showLoader();
+ await bookAppointmentsViewModel.getDoctorProfile(onSuccess: (dynamic respData) {
+ LoaderBottomSheet.hideLoader();
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: DoctorProfilePage(),
+ ),
+ );
+ }, onError: (err) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getErrorWidget(loadingText: err),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ });
+ },
+ backgroundColor: AppColors.bgRedLightColor,
+ borderColor: AppColors.primaryRedColor,
+ textColor: AppColors.primaryRedColor,
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 40.h,
+ ),
+ ),
+ SizedBox(width: 8.h),
+ Expanded(
+ flex: 1,
+ child: Container(
+ height: 40.h,
+ width: 40.w,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.textColor,
+ borderRadius: 12,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(12.h),
+ child: Transform.flip(
+ flipX: getIt().isArabic(),
+ child: Utils.buildSvgWithAssets(
+ icon: AppAssets.forward_arrow_icon_small,
+ iconColor: AppColors.whiteColor,
+ fit: BoxFit.contain,
+ ),
+ ),
+ ),
+ ).onPress(() async {
+ bookAppointmentsViewModel.setSelectedDoctor(DoctorsListResponseModel(
+ clinicID: doctor?.clinicID ?? 0,
+ projectID: doctor?.projectID ?? 0,
+ doctorID: doctor?.doctorID ?? 0,
+ ));
+ LoaderBottomSheet.showLoader();
+ await bookAppointmentsViewModel.getDoctorProfile(onSuccess: (dynamic respData) {
+ LoaderBottomSheet.hideLoader();
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: DoctorProfilePage(),
+ ),
+ );
+ }, onError: (err) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getErrorWidget(loadingText: err),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ });
+ }),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ );
+ }).toList(),
+ ),
+ )
+ : SizedBox.shrink(),
+ ),
+ ],
+ ),
+ );
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
@@ -191,8 +398,6 @@ class MyDoctorsPage extends StatelessWidget {
],
);
}),
- ),
- ),
);
}
}
diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart
index a35c1ca..50659ee 100644
--- a/lib/presentation/appointments/widgets/appointment_card.dart
+++ b/lib/presentation/appointments/widgets/appointment_card.dart
@@ -353,14 +353,14 @@ class AppointmentCard extends StatelessWidget {
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
- fontSize: 14.f,
+ fontSize: 12.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 40.h,
icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor,
- iconSize: 16.h,
+ iconSize: 14.h,
);
}
diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart
index 6b5dde7..ccf6674 100644
--- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart
+++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart
@@ -167,14 +167,14 @@ class AppointmentDoctorCard extends StatelessWidget {
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
- fontSize: 14.f,
+ fontSize: 12.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0),
height: 40.h,
icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor,
- iconSize: 16.h,
+ iconSize: 14.h,
);
} else {
return patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false
diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart
index 64884e5..1178691 100644
--- a/lib/presentation/home/landing_page.dart
+++ b/lib/presentation/home/landing_page.dart
@@ -108,17 +108,23 @@ class _LandingPageState extends State {
insuranceViewModel.initInsuranceProvider();
emergencyServicesViewModel.checkPatientERAdvanceBalance();
myAppointmentsViewModel.getPatientAppointmentQueueDetails();
+ if(!appState.isRatedVisible) {
appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) {
if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) {
appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!,
onSuccess: ((response) {
- appointmentRatingViewModel.setClinicOrDoctor(false);
- appointmentRatingViewModel.setTitle("Rate Doctor".needTranslation);
- appointmentRatingViewModel.setSubTitle("How was your last visit with doctor?".needTranslation);
- openLastRating();
+
+ appointmentRatingViewModel.setClinicOrDoctor(false);
+ appointmentRatingViewModel.setTitle("Rate Doctor".needTranslation);
+ appointmentRatingViewModel.setSubTitle("How was your last visit with doctor?".needTranslation);
+ openLastRating();
+ appState.setRatedVisible(true);
+
}));
+
}
});
+ }
}
});
super.initState();
diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart
index ebcd2e2..758914b 100644
--- a/lib/presentation/lab/lab_orders_page.dart
+++ b/lib/presentation/lab/lab_orders_page.dart
@@ -1 +1 @@
-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_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/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/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_order_by_test.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart';
import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_toolbar.dart';
import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import '../../widgets/appbar/collapsing_list_view.dart';
import 'alphabeticScroll.dart';
class LabOrdersPage extends StatefulWidget {
const LabOrdersPage({super.key});
@override
State createState() => _LabOrdersPageState();
}
class _LabOrdersPageState extends State {
late LabViewModel labProvider;
late DateRangeSelectorRangeViewModel rangeViewModel;
late AppState _appState;
List?> labSuggestions = [];
int? expandedIndex;
String? selectedFilterText = '';
int activeIndex = 0;
@override
void initState() {
scheduleMicrotask(() {
labProvider.initLabProvider();
});
super.initState();
}
@override
Widget build(BuildContext context) {
labProvider = Provider.of(context, listen: false);
rangeViewModel = Provider.of(context);
_appState = getIt();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingToolbar(
title: LocaleKeys.labResults.tr(),
search: () async {
final lavVM = Provider.of(context, listen: false);
if (lavVM.isLabOrdersLoading) {
return;
} else {
String? value = await Navigator.of(context).push(
CustomPageRoute(
page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions),
fullScreenDialog: true,
direction: AxisDirection.down,
),
);
if (value != null) {
selectedFilterText = value;
lavVM.filterLabReports(value);
}
}
},
child: SingleChildScrollView(
padding: EdgeInsets.all(24.h),
physics: NeverScrollableScrollPhysics(),
child: Consumer(
builder: (context, model, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, "By Visit".needTranslation),
CustomTabBarModel(null, "By Test".needTranslation),
// CustomTabBarModel(null, "Completed".needTranslation),
],
onTabChange: (index) {
activeIndex = index;
setState(() {});
},
),
SizedBox(height: 8.h),
selectedFilterText!.isNotEmpty
? CustomChipWidget(
chipText: selectedFilterText!,
chipType: ChipTypeEnum.alert,
isSelected: true,
)
: SizedBox(),
activeIndex == 0
? ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: model.isLabOrdersLoading
? 5
: model.patientLabOrders.isNotEmpty
? model.patientLabOrders.length
: 1,
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return model.isLabOrdersLoading
? LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
)
: model.patientLabOrders.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: LabResultItemView(
onTap: () {
model.currentlySelectedPatientOrder = model.patientLabOrders[index];
labProvider.getPatientLabResultByHospital(model.patientLabOrders[index]);
labProvider.getPatientSpecialResult(model.patientLabOrders[index]);
Navigator.push(
context,
CustomPageRoute(
page: LabResultByClinic(labOrder: model.patientLabOrders[index]),
));
},
labOrder: model.patientLabOrders[index],
index: index,
isExpanded: isExpanded),
),
),
)
: Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation);
},
)
// : ListView.builder(
// shrinkWrap: true,
// physics: NeverScrollableScrollPhysics(),
// padding: EdgeInsets.zero,
// itemCount: model.isLabOrdersLoading
// ? 5
// : model.uniqueTests.toList().isNotEmpty
// ? model.uniqueTests.toList().length
// : 1,
// itemBuilder: (context, index) {
// final isExpanded = expandedIndex == index;
// return model.isLabOrdersLoading
// ? LabResultItemView(
// onTap: () {},
// labOrder: null,
// index: index,
// isLoading: true,
// )
// : model.uniqueTests.toList().isNotEmpty
// ? AnimationConfiguration.staggeredList(
// position: index,
// duration: const Duration(milliseconds: 500),
// child: SlideAnimation(
// verticalOffset: 100.0,
// child: FadeInAnimation(
// child: LabOrderByTest(
// appState: _appState,
// onTap: () {
// if (model.uniqueTests.toList()[index].model != null) {
// rangeViewModel.flush();
// model.getPatientLabResult(model.uniqueTests.toList()[index].model!, model.uniqueTests.toList()[index].description!,
// (_appState.isArabic() ? model.uniqueTests.toList()[index].testDescriptionAr! : model.uniqueTests.toList()[index].testDescriptionEn!));
// }
// },
// tests: model.uniqueTests.toList()[index],
// index: index,
// isExpanded: isExpanded)),
// ),
// )
// : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation);
// },
// )
: (model.isLabOrdersLoading)
? Column(
children: List.generate(
5,
(index) => LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
)),
)
:AlphabeticScroll(
alpahbetsAvailable: model.indexedCharacterForUniqueTest,
details: model.uniqueTestsList,
labViewModel: model,
rangeViewModel: rangeViewModel,
appState: _appState,
)
],
);
},
),
),
));
}
Color getLabOrderStatusColor(num status) {
switch (status) {
case 44:
return AppColors.warningColorYellow;
case 45:
return AppColors.warningColorYellow;
case 16:
return AppColors.successColor;
case 17:
return AppColors.successColor;
default:
return AppColors.greyColor;
}
}
String getLabOrderStatusText(num status) {
switch (status) {
case 44:
return LocaleKeys.resultsPending.tr(context: context);
case 45:
return LocaleKeys.resultsPending.tr(context: context);
case 16:
return LocaleKeys.resultsAvailable.tr(context: context);
case 17:
return LocaleKeys.resultsAvailable.tr(context: context);
default:
return "";
}
}
getLabSuggestions(LabViewModel model) {
if (model.patientLabOrders.isEmpty) {
return [];
}
return model.patientLabOrders.map((m) => m.testDetails).toList();
}
}
\ No newline at end of file
+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_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart';
import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_toolbar.dart';
import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import 'alphabeticScroll.dart';
class LabOrdersPage extends StatefulWidget {
const LabOrdersPage({super.key});
@override
State createState() => _LabOrdersPageState();
}
class _LabOrdersPageState extends State {
late LabViewModel labProvider;
late DateRangeSelectorRangeViewModel rangeViewModel;
late AppState _appState;
List?> labSuggestions = [];
int? expandedIndex;
String? selectedFilterText = '';
int activeIndex = 0;
// Scroll controller to control page scrolling when a group expands
late ScrollController _scrollController;
// Map of keys for each group to allow scrolling to them
final Map _groupKeys = {};
@override
void initState() {
_scrollController = ScrollController();
scheduleMicrotask(() {
labProvider.initLabProvider();
});
super.initState();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
labProvider = Provider.of(context, listen: false);
rangeViewModel = Provider.of(context);
_appState = getIt();
return CollapsingToolbar(
title: LocaleKeys.labResults.tr(),
search: () async {
final lavVM = Provider.of(context, listen: false);
if (lavVM.isLabOrdersLoading) {
return;
} else {
String? value = await Navigator.of(context).push(
CustomPageRoute(
page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions),
fullScreenDialog: true,
direction: AxisDirection.down,
),
);
if (value != null) {
selectedFilterText = value;
lavVM.filterLabReports(value);
}
}
},
child: SingleChildScrollView(
controller: _scrollController,
padding: EdgeInsets.all(24.h),
physics: AlwaysScrollableScrollPhysics(),
child: Consumer(
builder: (context, model, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, "By Visit".needTranslation),
CustomTabBarModel(null, "By Test".needTranslation),
// CustomTabBarModel(null, "Completed".needTranslation),
],
onTabChange: (index) {
activeIndex = index;
setState(() {});
},
),
),
],
),
// Place Clinic/Hospital buttons under the tabs, styled like prescriptions
if (activeIndex == 0)
Padding(
padding: EdgeInsets.symmetric(vertical: 10.h),
child: 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.withValues(alpha: 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.withValues(alpha: 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: 8.h),
selectedFilterText!.isNotEmpty
? CustomChipWidget(
chipText: selectedFilterText!,
chipType: ChipTypeEnum.alert,
isSelected: true,
)
: SizedBox(),
activeIndex == 0
? // By Visit - show grouped view when available
model.isLabOrdersLoading
? ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: 5,
itemBuilder: (context, index) => LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
),
)
: (model.patientLabOrdersViewList.isNotEmpty
? ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: model.patientLabOrdersViewList.length,
itemBuilder: (context, index) {
final group = model.patientLabOrdersViewList[index];
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 400),
child: SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(
child: AnimatedContainer(
key: _groupKeys.putIfAbsent(index, () => GlobalKey()),
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 = expandedIndex == index ? null : index;
});
// After rebuild, ensure the expanded group is visible
WidgetsBinding.instance.addPostFrameCallback((_) {
final key = _groupKeys[index];
if (key != null && key.currentContext != null && expandedIndex == index) {
Scrollable.ensureVisible(
key.currentContext!,
duration: Duration(milliseconds: 350),
curve: Curves.easeInOut,
alignment: 0.1,
);
}
});
},
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomButton(
text: "${group.length} ${'results'.needTranslation}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
Icon(expandedIndex == index ? Icons.expand_less : Icons.expand_more, ),
],
),
SizedBox(height: 8.h),
Text(
model.isSortByClinic ? (group.first.clinicDescription ?? 'Unknown') : (group.first.projectName ?? 'Unknown'),
style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600),
overflow: TextOverflow.ellipsis,
),
// Expanded list of orders when group is expanded
AnimatedSwitcher(
duration: Duration(milliseconds: 400),
child: expandedIndex == index
? Container(
key: ValueKey(index),
padding: EdgeInsets.only(top: 12.h),
child: Column(
children: group.map((order) {
return Container(
margin: EdgeInsets.only(bottom: 12.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 12.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
order.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 24.h,
height: 24.h,
fit: BoxFit.cover,
).circle(100),
SizedBox(width: 8.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(order.doctorName ?? order.doctorNameEnglish ?? "").toString().toText14(weight: FontWeight.w500),
SizedBox(height: 6.h),
],
),
),
],
),
SizedBox(height: 8.h),
Row(
children: [
CustomButton(
text: ("Order No: ".needTranslation + order.orderNo!),
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: 24.h,
),
SizedBox(width: 8.h),
CustomButton(
text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(order.orderDate ?? ""), false),
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: 24.h,
),
SizedBox(width: 8.h),
],
),
SizedBox(height: 8.h),
Row(
children: [
CustomButton(
text: model.isSortByClinic ? (order.clinicDescription ?? "") : (order.projectName ?? ""),
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: 24.h,
),
// SizedBox(height: 8.h),
// Expanded(
// flex: 6,
// child: CustomButton(
// text: LocaleKeys.view.tr(context: context),
// onPressed: () async {
// model.currentlySelectedPatientOrder = order;
// labProvider.getPatientLabResultByHospital(order);
// labProvider.getPatientSpecialResult(order);
// Navigator.push(
// context,
// CustomPageRoute(
// page: LabResultByClinic(labOrder: order),
// ),
// );
// },
// backgroundColor: AppColors.bgRedLightColor,
// borderColor: AppColors.primaryRedColor,
// textColor: AppColors.primaryRedColor,
// fontSize: 14,
// fontWeight: FontWeight.w500,
// borderRadius: 12,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 40.h,
// ),
// ),
],
),
Row(
children: [
Expanded(flex:6, child: SizedBox()),
Expanded(
flex: 1,
child: Container(
height: 40.h,
width: 40.w,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.textColor,
borderRadius: 12,
),
child: Padding(
padding: EdgeInsets.all(12.h),
child: Transform.flip(
flipX: _appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.whiteColor,
fit: BoxFit.contain,
),
),
),
).onPress(() {
model.currentlySelectedPatientOrder = order;
labProvider.getPatientLabResultByHospital(order);
labProvider.getPatientSpecialResult(order);
Navigator.of(context).push(
CustomPageRoute(page: LabResultByClinic(labOrder: order)),
);
}),
)
],
),
])),
);
}).toList(),
),
)
: SizedBox.shrink(),
),
],
),
),
),
),
),
),
);
},
)
: Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation))
: // By Test or other tabs keep existing behavior
(model.isLabOrdersLoading)
? Column(
children: List.generate(
5,
(index) => LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
)),
)
: AlphabeticScroll(
alpahbetsAvailable: model.indexedCharacterForUniqueTest,
details: model.uniqueTestsList,
labViewModel: model,
rangeViewModel: rangeViewModel,
appState: _appState,
)
],
);
},
),
),
);
}
Color getLabOrderStatusColor(num status) {
switch (status) {
case 44:
return AppColors.warningColorYellow;
case 45:
return AppColors.warningColorYellow;
case 16:
return AppColors.successColor;
case 17:
return AppColors.successColor;
default:
return AppColors.greyColor;
}
}
String getLabOrderStatusText(num status) {
switch (status) {
case 44:
return LocaleKeys.resultsPending.tr(context: context);
case 45:
return LocaleKeys.resultsPending.tr(context: context);
case 16:
return LocaleKeys.resultsAvailable.tr(context: context);
case 17:
return LocaleKeys.resultsAvailable.tr(context: context);
default:
return "";
}
}
getLabSuggestions(LabViewModel model) {
if (model.patientLabOrders.isEmpty) {
return [];
}
return model.patientLabOrders.map((m) => m.testDetails).toList();
}
}
\ No newline at end of file
diff --git a/lib/presentation/medical_file/patient_sickleaves_list_page.dart b/lib/presentation/medical_file/patient_sickleaves_list_page.dart
index 709e4e3..cbd7b56 100644
--- a/lib/presentation/medical_file/patient_sickleaves_list_page.dart
+++ b/lib/presentation/medical_file/patient_sickleaves_list_page.dart
@@ -3,6 +3,10 @@ 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/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.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';
@@ -12,6 +16,9 @@ import 'package:hmg_patient_app_new/features/medical_file/models/patient_sicklea
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
+import 'package:open_filex/open_filex.dart';
import 'package:provider/provider.dart';
import 'widgets/patient_sick_leave_card.dart';
@@ -24,6 +31,7 @@ class PatientSickleavesListPage extends StatefulWidget {
}
class _PatientSickleavesListPageState extends State {
+ int? expandedIndex;
late MedicalFileViewModel medicalFileViewModel;
@override
@@ -37,53 +45,257 @@ class _PatientSickleavesListPageState extends State {
@override
Widget build(BuildContext context) {
+ AppState appState = getIt.get();
medicalFileViewModel = Provider.of(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: "${LocaleKeys.sick.tr(context: context)} ${LocaleKeys.sickSubtitle.tr(context: context)}",
child: SingleChildScrollView(
- child: Consumer(builder: (context, medicalFileVM, child) {
+ child: Consumer(builder: (context, model, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- SizedBox(height: 24.h),
- ListView.separated(
- padding: EdgeInsets.zero,
- scrollDirection: Axis.vertical,
- itemCount: medicalFileVM.isPatientSickLeaveListLoading
- ? 3
- : medicalFileVM.patientSickLeaveList.isNotEmpty
- ? medicalFileVM.patientSickLeaveList.length
+ SizedBox(height: 16.h),
+ // Clinic & Hospital Sort
+ Row(
+ children: [
+ CustomButton(
+ text: LocaleKeys.byClinic.tr(context: context),
+ onPressed: () {
+ model.setIsSickLeavesSortByClinic(true);
+ },
+ backgroundColor: model.isSickLeavesSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
+ borderColor: model.isSickLeavesSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
+ textColor: model.isSickLeavesSortByClinic ? 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.setIsSickLeavesSortByClinic(false);
+ },
+ backgroundColor: model.isSickLeavesSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
+ borderColor: model.isSickLeavesSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor,
+ textColor: model.isSickLeavesSortByClinic ? 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.isPatientSickLeaveListLoading
+ ? 4
+ : model.patientSickLeaveList.isNotEmpty
+ ? model.patientSickLeavesViewList.length
: 1,
- shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
+ shrinkWrap: true,
+ padding: const EdgeInsets.only(left: 0, right: 8),
itemBuilder: (context, index) {
- return medicalFileVM.isPatientSickLeaveListLoading
+ final isExpanded = expandedIndex == index;
+ return model.isPatientSickLeaveListLoading
? PatientSickLeaveCard(
patientSickLeavesResponseModel: PatientSickLeavesResponseModel(),
isLoading: true,
).paddingSymmetrical(24.h, 0.0)
- : medicalFileVM.patientSickLeaveList.isNotEmpty
+ : model.patientSickLeaveList.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
- duration: const Duration(milliseconds: 1000),
+ duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
- child: PatientSickLeaveCard(
- patientSickLeavesResponseModel: medicalFileVM.patientSickLeaveList.first,
- isLoading: false,
- isSickLeaveListPage: true,
- ).paddingSymmetrical(24.h, 0.0),
+ 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.patientSickLeavesViewList[index].sickLeavesList!.length} ${LocaleKeys.sickSubtitle.tr(context: context)} 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.patientSickLeavesViewList[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.patientSickLeavesViewList[index].sickLeavesList!.map((sickLeave) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Image.network(
+ sickLeave.doctorImageURL!,
+ width: 24.h,
+ height: 24.h,
+ fit: BoxFit.fill,
+ ).circle(100),
+ SizedBox(width: 8.h),
+ Expanded(child: sickLeave.doctorName!.toText14(weight: FontWeight.w500)),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ Row(
+ children: [
+ CustomButton(
+ text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(sickLeave.appointmentDate), false),
+ 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: 24.h,
+ ),
+ SizedBox(width: 8.h),
+ CustomButton(
+ text: model.isSickLeavesSortByClinic ? sickLeave.projectName! : sickLeave.clinicName!,
+ 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: 24.h,
+ ),
+ SizedBox(width: 8.h),
+ CustomButton(
+ text: "${sickLeave.sickLeaveDays} Days",
+ 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: 24.h,
+ ),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ Row(
+ children: [
+ Expanded(
+ flex: 6,
+ child: CustomButton(
+ text: "Download Report".needTranslation,
+ onPressed: () async {
+ LoaderBottomSheet.showLoader();
+ await medicalFileViewModel.getPatientSickLeavePDF(sickLeave, appState.getAuthenticatedUser()!).then((val) async {
+ LoaderBottomSheet.hideLoader();
+ if (medicalFileViewModel.patientSickLeavePDFBase64.isNotEmpty) {
+ String path = await Utils.createFileFromString(medicalFileViewModel.patientSickLeavePDFBase64, "pdf");
+ try {
+ OpenFilex.open(path);
+ } catch (ex) {
+ debugPrint("Error opening file: $ex");
+ }
+ }
+ });
+ },
+ backgroundColor: AppColors.secondaryLightRedColor,
+ borderColor: AppColors.secondaryLightRedColor,
+ textColor: AppColors.primaryRedColor,
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 40.h,
+ icon: AppAssets.download,
+ iconColor: AppColors.primaryRedColor,
+ iconSize: 14.h,
+ ),
+ ),
+ ],
+ ),
+ SizedBox(height: 12.h),
+ Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h),
+ SizedBox(height: 12.h),
+ ],
+ );
+ }),
+ ],
+ ),
+ )
+ : SizedBox.shrink(),
+ ),
+ ],
+ ),
+ ),
+ ),
),
),
)
: Utils.getNoDataWidget(context, noDataText: "You don't have any sick leaves yet.".needTranslation);
},
- separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 8.h),
- ),
- SizedBox(height: 60.h),
+ ).paddingSymmetrical(24.h, 0.h),
],
);
}),
diff --git a/lib/presentation/medical_report/medical_reports_page.dart b/lib/presentation/medical_report/medical_reports_page.dart
index 4047a53..b15266c 100644
--- a/lib/presentation/medical_report/medical_reports_page.dart
+++ b/lib/presentation/medical_report/medical_reports_page.dart
@@ -2,6 +2,9 @@ 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/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.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';
@@ -17,6 +20,7 @@ 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:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
+import 'package:open_filex/open_filex.dart';
import 'package:provider/provider.dart';
class MedicalReportsPage extends StatefulWidget {
@@ -27,10 +31,12 @@ class MedicalReportsPage extends StatefulWidget {
}
class _MedicalReportsPageState extends State {
+ int? expandedIndex;
late MedicalFileViewModel medicalFileViewModel;
@override
Widget build(BuildContext context) {
+ AppState appState = getIt.get();
medicalFileViewModel = Provider.of(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
@@ -44,16 +50,53 @@ class _MedicalReportsPageState extends State {
return Column(
children: [
SizedBox(height: 16.h),
+ // Status filter tabs
+ Row(
+ children: [
+ CustomButton(
+ text: LocaleKeys.byClinic.tr(context: context),
+ onPressed: () {
+ medicalFileVM.setIsMedicalReportsSortByClinic(true);
+ },
+ backgroundColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
+ borderColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
+ textColor: medicalFileVM.isMedicalReportsSortByClinic ? 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: () {
+ medicalFileVM.setIsMedicalReportsSortByClinic(false);
+ },
+ backgroundColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
+ borderColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor,
+ textColor: medicalFileVM.isMedicalReportsSortByClinic ? 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),
Row(
children: [
CustomButton(
text: "Requested".needTranslation,
onPressed: () {
+ setState(() {
+ expandedIndex = null;
+ });
medicalFileViewModel.onMedicalReportTabChange(0);
},
backgroundColor: medicalFileVM.selectedMedicalReportsTabIndex == 0 ? AppColors.bgRedLightColor : AppColors.whiteColor,
- borderColor:
- medicalFileVM.selectedMedicalReportsTabIndex == 0 ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2),
+ borderColor: medicalFileVM.selectedMedicalReportsTabIndex == 0 ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
textColor: medicalFileVM.selectedMedicalReportsTabIndex == 0 ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
@@ -65,11 +108,13 @@ class _MedicalReportsPageState extends State {
CustomButton(
text: LocaleKeys.ready.tr(context: context),
onPressed: () {
+ setState(() {
+ expandedIndex = null;
+ });
medicalFileViewModel.onMedicalReportTabChange(1);
},
backgroundColor: medicalFileVM.selectedMedicalReportsTabIndex == 1 ? AppColors.bgRedLightColor : AppColors.whiteColor,
- borderColor:
- medicalFileVM.selectedMedicalReportsTabIndex == 1 ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2),
+ borderColor: medicalFileVM.selectedMedicalReportsTabIndex == 1 ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
textColor: medicalFileVM.selectedMedicalReportsTabIndex == 1 ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
@@ -81,11 +126,13 @@ class _MedicalReportsPageState extends State {
CustomButton(
text: LocaleKeys.cancelled.tr(context: context),
onPressed: () {
+ setState(() {
+ expandedIndex = null;
+ });
medicalFileViewModel.onMedicalReportTabChange(2);
},
backgroundColor: medicalFileVM.selectedMedicalReportsTabIndex == 2 ? AppColors.bgRedLightColor : AppColors.whiteColor,
- borderColor:
- medicalFileVM.selectedMedicalReportsTabIndex == 2 ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2),
+ borderColor: medicalFileVM.selectedMedicalReportsTabIndex == 2 ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
textColor: medicalFileVM.selectedMedicalReportsTabIndex == 2 ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
@@ -95,34 +142,27 @@ class _MedicalReportsPageState extends State {
),
],
).paddingSymmetrical(24.h, 0.h),
- // CustomTabBar(
- // activeTextColor: Color(0xffED1C2B),
- // activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
- // tabs: [
- // CustomTabBarModel(null, "Requested".needTranslation),
- // CustomTabBarModel(null, "Ready".needTranslation),
- // CustomTabBarModel(null, "Cancelled".needTranslation),
- // ],
- // onTabChange: (index) {
- // medicalFileViewModel.onMedicalReportTabChange(index);
- // },
- // ).paddingSymmetrical(24.h, 0.h),
- ListView.separated(
- padding: EdgeInsets.only(top: 24.h),
- shrinkWrap: true,
- physics: NeverScrollableScrollPhysics(),
+ SizedBox(height: 12.h),
+ // Clinic & Hospital Sort
+
+ // Expandable list
+ ListView.builder(
itemCount: medicalFileViewModel.isPatientMedicalReportsListLoading
- ? 3
+ ? 4
: medicalFileViewModel.patientMedicalReportList.isNotEmpty
- ? medicalFileViewModel.patientMedicalReportList.length
+ ? medicalFileViewModel.patientMedicalReportsViewList.length
: 1,
+ physics: NeverScrollableScrollPhysics(),
+ shrinkWrap: true,
+ padding: const EdgeInsets.only(left: 0, right: 8),
itemBuilder: (context, index) {
+ final isExpanded = expandedIndex == index;
return medicalFileViewModel.isPatientMedicalReportsListLoading
? PatientMedicalReportCard(
patientMedicalReportResponseModel: PatientMedicalReportResponseModel(),
medicalFileViewModel: medicalFileVM,
isLoading: true,
- ).paddingSymmetrical(24.h, 0.h)
+ ).paddingSymmetrical(24.h, 8.h)
: medicalFileViewModel.patientMedicalReportList.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
@@ -133,22 +173,185 @@ class _MedicalReportsPageState extends State {
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
- decoration: RoundedRectangleBorder()
- .toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
- child: PatientMedicalReportCard(
- patientMedicalReportResponseModel: medicalFileVM.patientMedicalReportList[index],
- medicalFileViewModel: medicalFileVM,
- isLoading: false,
+ 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: "${medicalFileVM.patientMedicalReportsViewList[index].medicalReportsList!.length} Reports 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),
+ medicalFileVM.patientMedicalReportsViewList[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: [
+ ...medicalFileVM.patientMedicalReportsViewList[index].medicalReportsList!.map((report) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Image.network(
+ report.doctorImageURL!,
+ width: 24.h,
+ height: 24.h,
+ fit: BoxFit.fill,
+ ).circle(100),
+ SizedBox(width: 8.h),
+ Expanded(child: report.doctorName!.toText14(weight: FontWeight.w500)),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ Row(
+ children: [
+ CustomButton(
+ text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(report.appointmentDate), false),
+ 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: 24.h,
+ ),
+ SizedBox(width: 8.h),
+ CustomButton(
+ text: medicalFileVM.isMedicalReportsSortByClinic ? report.projectName! : report.clinicDescription!,
+ 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: 24.h,
+ ),
+ SizedBox(width: 8.h),
+ CustomButton(
+ text: report.statusDesc!,
+ 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: 24.h,
+ ),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ Row(
+ children: [
+ if (medicalFileVM.selectedMedicalReportsTabIndex == 1)
+ Expanded(
+ flex: 6,
+ child: CustomButton(
+ text: "Download Report".needTranslation,
+ onPressed: () async {
+ LoaderBottomSheet.showLoader();
+ await medicalFileViewModel.getPatientMedicalReportPDF(report, appState.getAuthenticatedUser()!).then((val) async {
+ LoaderBottomSheet.hideLoader();
+ if (medicalFileViewModel.patientMedicalReportPDFBase64.isNotEmpty) {
+ String path = await Utils.createFileFromString(medicalFileViewModel.patientMedicalReportPDFBase64, "pdf");
+ try {
+ OpenFilex.open(path);
+ } catch (ex) {
+ debugPrint("Error opening file: $ex");
+ }
+ }
+ });
+ },
+ backgroundColor: AppColors.secondaryLightRedColor,
+ borderColor: AppColors.secondaryLightRedColor,
+ textColor: AppColors.primaryRedColor,
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 40.h,
+ icon: AppAssets.download,
+ iconColor: AppColors.primaryRedColor,
+ iconSize: 14.h,
+ ),
+ ),
+ ],
+ ),
+ SizedBox(height: 12.h),
+ Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h),
+ SizedBox(height: 12.h),
+ ],
+ );
+ }),
+ ],
+ ),
+ )
+ : SizedBox.shrink(),
+ ),
+ ],
+ ),
),
- ).paddingSymmetrical(24.h, 0.h),
+ ),
),
),
)
: Utils.getNoDataWidget(context, noDataText: "You don't have any medical reports yet.".needTranslation)
.paddingSymmetrical(24.h, 24.h);
},
- separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
- ),
+ ).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 24.h),
],
);
diff --git a/lib/presentation/radiology/radiology_orders_page.dart b/lib/presentation/radiology/radiology_orders_page.dart
index cb925ef..c0eb782 100644
--- a/lib/presentation/radiology/radiology_orders_page.dart
+++ b/lib/presentation/radiology/radiology_orders_page.dart
@@ -3,13 +3,13 @@ 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/lab/lab_view_model.dart';
+import 'package:hmg_patient_app_new/features/radiology/models/resp_models/patient_radiology_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/radiology/radiology_result_page.dart';
@@ -22,6 +22,7 @@ import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import '../../features/radiology/radiology_view_model.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
class RadiologyOrdersPage extends StatefulWidget {
const RadiologyOrdersPage({super.key});
@@ -34,226 +35,401 @@ class _RadiologyOrdersPageState extends State {
late RadiologyViewModel radiologyViewModel;
String selectedFilterText = '';
int? expandedIndex;
+ // Scroll controller to ensure expanded group is visible
+ late ScrollController _scrollController;
+ final Map _groupKeys = {};
@override
void initState() {
+ _scrollController = ScrollController();
scheduleMicrotask(() {
radiologyViewModel.initRadiologyViewModel();
});
super.initState();
}
+ @override
+ void dispose() {
+ _scrollController.dispose();
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
radiologyViewModel = Provider.of(context);
return Scaffold(
- backgroundColor: AppColors.bgScaffoldColor,
- body: CollapsingListView(
- title: LocaleKeys.radiology.tr(context: context),
- search: () async {
- final lavVM = Provider.of(context, listen: false);
- if (lavVM.isLabOrdersLoading) {
- return;
- } else {
- String? value = await Navigator.of(context).push(
- CustomPageRoute(
- page: SearchRadiologyContent(radiologySuggestionsList: radiologyViewModel.radiologySuggestions),
- fullScreenDialog: true,
- direction: AxisDirection.down,
- ),
- );
- if (value != null) {
- selectedFilterText = value;
- radiologyViewModel.filterRadiologyReports(value);
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: CollapsingListView(
+ title: LocaleKeys.radiology.tr(context: context),
+ search: () async {
+ final lavVM = Provider.of(context, listen: false);
+ if (lavVM.isLabOrdersLoading) {
+ return;
+ } else {
+ String? value = await Navigator.of(context).push(
+ CustomPageRoute(
+ page: SearchRadiologyContent(radiologySuggestionsList: radiologyViewModel.radiologySuggestions),
+ fullScreenDialog: true,
+ direction: AxisDirection.down,
+ ),
+ );
+ if (value != null) {
+ selectedFilterText = value;
+ radiologyViewModel.filterRadiologyReports(value);
+ }
}
- }
- },
- child: SingleChildScrollView(
- child: Consumer(
- builder: (context, model, child) {
- return Padding(
- padding: EdgeInsets.symmetric(horizontal: 24.h),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- selectedFilterText!.isNotEmpty
- ? AppCustomChipWidget(
- padding: EdgeInsets.symmetric(horizontal: 5.h),
- labelText: selectedFilterText!,
- deleteIcon: 'assets/images/svg/cross_circle.svg',
- backgroundColor: AppColors.alertColor,
- textColor: AppColors.whiteColor,
- deleteIconColor: AppColors.whiteColor,
- deleteIconHasColor: true,
- onDeleteTap: () {
- setState(() {
- selectedFilterText = '';
- model.filterRadiologyReports('');
- });
+ },
+ child: SingleChildScrollView(
+ controller: _scrollController,
+ physics: NeverScrollableScrollPhysics(),
+ child: Consumer(
+ builder: (context, model, child) {
+ // Build grouping lists if we have data and none constructed yet
+ if (!model.isRadiologyOrdersLoading && model.patientRadiologyOrders.isNotEmpty && model.patientRadiologyOrdersViewList.isEmpty) {
+ final clinicMap = >{};
+ final hospitalMap = >{};
+ for (var order in model.patientRadiologyOrders) {
+ final clinicKey = (order.clinicDescription ?? 'Unknown').trim();
+ clinicMap.putIfAbsent(clinicKey, () => []).add(order);
+ final hospitalKey = (order.projectName ?? order.projectID ?? 'Unknown').toString().trim();
+ hospitalMap.putIfAbsent(hospitalKey, () => []).add(order);
+ }
+ model.patientRadiologyOrdersByClinic = clinicMap.values.toList();
+ model.patientRadiologyOrdersByHospital = hospitalMap.values.toList();
+ model.patientRadiologyOrdersViewList = model.isSortByClinic ? model.patientRadiologyOrdersByClinic : model.patientRadiologyOrdersByHospital;
+ }
+
+ return Padding(
+ padding: EdgeInsets.symmetric(horizontal: 24.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Clinic / Hospital toggle
+ SizedBox(height: 16.h),
+ 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.withValues(alpha: 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);
},
- // chipType: ChipTypeEnum.alert,
- // isSelected: true,
- )
- : SizedBox(),
- ListView.builder(
- shrinkWrap: true,
- physics: NeverScrollableScrollPhysics(),
- itemCount: model.isRadiologyOrdersLoading
- ? 5
- : model.patientRadiologyOrders.isNotEmpty
- ? model.patientRadiologyOrders.length
- : 1,
- itemBuilder: (context, index) {
- final isExpanded = expandedIndex == index;
- return model.isRadiologyOrdersLoading
- ? LabResultItemView(
- onTap: () {},
- labOrder: null,
- index: index,
- isLoading: true,
- )
- : model.patientRadiologyOrders.isNotEmpty
- ? AnimationConfiguration.staggeredList(
- position: index,
- duration: const Duration(milliseconds: 500),
- child: SlideAnimation(
- verticalOffset: 100.0,
- child: FadeInAnimation(
- child: AnimatedContainer(
- duration: Duration(milliseconds: 300),
- curve: Curves.easeInOut,
- margin: EdgeInsets.symmetric(vertical: 8.h),
- decoration: RoundedRectangleBorder()
- .toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
- child: InkWell(
- onTap: () {
- setState(() {
- expandedIndex = isExpanded ? null : index;
+ backgroundColor: model.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
+ borderColor: model.isSortByClinic ? AppColors.textColor.withValues(alpha: 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: 8.h),
+ selectedFilterText.isNotEmpty
+ ? AppCustomChipWidget(
+ padding: EdgeInsets.symmetric(horizontal: 5.h),
+ labelText: selectedFilterText,
+ deleteIcon: 'assets/images/svg/cross_circle.svg',
+ backgroundColor: AppColors.alertColor,
+ textColor: AppColors.whiteColor,
+ deleteIconColor: AppColors.whiteColor,
+ deleteIconHasColor: true,
+ onDeleteTap: () {
+ setState(() {
+ selectedFilterText = '';
+ model.filterRadiologyReports('');
+ });
+ },
+ // chipType: ChipTypeEnum.alert,
+ // isSelected: true,
+ )
+ : SizedBox(),
+
+ // Grouped view when available
+ Builder(builder: (ctx) {
+ if (model.isRadiologyOrdersLoading) {
+ return ListView.builder(
+ shrinkWrap: true,
+ physics: NeverScrollableScrollPhysics(),
+ padding: EdgeInsets.zero,
+ itemCount: 5,
+ itemBuilder: (context, index) => LabResultItemView(
+ onTap: () {},
+ labOrder: null,
+ index: index,
+ isLoading: true,
+ ),
+ );
+ }
+
+ if (model.patientRadiologyOrdersViewList.isEmpty) {
+ return Utils.getNoDataWidget(ctx, noDataText: "You don't have any radiology results yet.".needTranslation);
+ }
+
+ return ListView.builder(
+ shrinkWrap: true,
+ physics: NeverScrollableScrollPhysics(),
+ padding: EdgeInsets.zero,
+ itemCount: model.patientRadiologyOrdersViewList.length,
+ itemBuilder: (context, index) {
+ final group = model.patientRadiologyOrdersViewList[index];
+ final displayName = model.isSortByClinic ? (group.first.clinicDescription ?? 'Unknown') : (group.first.projectName ?? 'Unknown');
+ return AnimationConfiguration.staggeredList(
+ position: index,
+ duration: const Duration(milliseconds: 400),
+ child: SlideAnimation(
+ verticalOffset: 50.0,
+ child: FadeInAnimation(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Group header container with key so we can scroll to it
+ GestureDetector(
+ onTap: () {
+ setState(() {
+ expandedIndex = expandedIndex == index ? null : index;
+ });
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ final key = _groupKeys.putIfAbsent(index, () => GlobalKey());
+ if (key.currentContext != null && expandedIndex == index) {
+ // Delay scrolling to wait for expansion animation
+ Future.delayed(Duration(milliseconds: 450), () {
+ if (key.currentContext != null) {
+ Scrollable.ensureVisible(
+ key.currentContext!,
+ duration: Duration(milliseconds: 350),
+ curve: Curves.easeInOut,
+ alignment: 0.0,
+ );
+ }
});
- },
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Padding(
- padding: EdgeInsets.all(16.h),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- AppCustomChipWidget(
- labelText: LocaleKeys.resultsAvailable.tr(context: context),
- backgroundColor: AppColors.successColor.withOpacity(0.15),
- textColor: AppColors.successColor,
- ).toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 100),
- SizedBox(height: 8.h),
- Row(
- children: [
- Image.network(
- model.isRadiologyOrdersLoading
- ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png"
- : model.patientRadiologyOrders[index].doctorImageURL!,
- width: 24.h,
- height: 24.h,
- fit: BoxFit.fill,
- ).circle(100).toShimmer2(isShow: model.isRadiologyOrdersLoading),
- SizedBox(width: 4.h),
- (model.isRadiologyOrdersLoading
- ? "Dr John Smith"
- : model.patientRadiologyOrders[index].doctorName!)
- .toText16(isBold: true)
- .toShimmer2(isShow: model.isRadiologyOrdersLoading)
- ],
+ }
+ });
+ },
+ child: Container(
+ key: _groupKeys.putIfAbsent(index, () => GlobalKey()),
+ margin: EdgeInsets.only(bottom: 8.h),
+ padding: EdgeInsets.all(12.h),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(8.h),
+ boxShadow: [
+ BoxShadow(
+ color: AppColors.blackColor.withValues(alpha: 0.03),
+ blurRadius: 6,
+ offset: Offset(0, 2),
+ )
+ ],
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ CustomButton(
+ text: "${group.length} ${'results'.needTranslation}",
+ onPressed: () {},
+ backgroundColor: AppColors.greyColor,
+ borderColor: AppColors.greyColor,
+ textColor: AppColors.blackColor,
+ fontSize: 10,
+ fontWeight: FontWeight.w500,
+ borderRadius: 8,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 30.h,
+ ),
+ Icon(expandedIndex == index ? Icons.expand_less : Icons.expand_more),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ Text(
+ displayName,
+ style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600),
+ overflow: TextOverflow.ellipsis,
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ AnimatedSwitcher(
+ duration: Duration(milliseconds: 400),
+ child: expandedIndex == index
+ ? Container(
+ key: ValueKey(index),
+ padding: EdgeInsets.only(top: 12.h),
+ child: Column(
+ children: group.map((order) {
+ return Container(
+ margin: EdgeInsets.only(bottom: 12.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 12.h,
+ hasShadow: true,
),
- SizedBox(height: 8.h),
- Wrap(
- direction: Axis.horizontal,
- spacing: 3.h,
- runSpacing: 4.h,
- children: [
- AppCustomChipWidget(
- icon: AppAssets.doctor_calendar_icon,
- labelText: model.isRadiologyOrdersLoading
- ? "01 Jan 2025"
- : DateUtil.formatDateToDate(model.patientRadiologyOrders[index].orderDate!, false),
- ).toShimmer2(isShow: model.isRadiologyOrdersLoading),
- AppCustomChipWidget(
- labelText: model.isRadiologyOrdersLoading
- ? "01 Jan 2025"
- : model.patientRadiologyOrders[index].clinicDescription!,
- ).toShimmer2(isShow: model.isRadiologyOrdersLoading),
+ child: Padding(
+ padding: EdgeInsets.all(14.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Image.network(
+ order.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
+ width: 24.h,
+ height: 24.h,
+ fit: BoxFit.cover,
+ ).circle(100),
+ SizedBox(width: 8.h),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
- // AppCustomChipWidget(labelText: "").toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 16.h),
- // AppCustomChipWidget(labelText: "").toShimmer2(isShow: model.isRadiologyOrdersLoading, width: 16.h),
- ],
- ),
- ],
- ),
- ),
- model.isRadiologyOrdersLoading
- ? SizedBox.shrink()
- : AnimatedCrossFade(
- firstChild: SizedBox.shrink(),
- secondChild: Padding(
- padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Padding(
- padding: EdgeInsets.only(bottom: 8.h),
- child: '● ${model.patientRadiologyOrders[index].description}'
- .toText14(weight: FontWeight.w500),
- ),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- SizedBox(),
- CustomButton(
- icon: AppAssets.view_report_icon,
- iconColor: AppColors.primaryRedColor,
- iconSize: 16.h,
- text: LocaleKeys.viewReport.tr(context: context),
- onPressed: () {
- Navigator.of(context).push(
- CustomPageRoute(
- page: RadiologyResultPage(
- patientRadiologyResponseModel: model.patientRadiologyOrders[index]),
- ),
- );
- },
- backgroundColor: AppColors.secondaryLightRedColor,
- borderColor: AppColors.secondaryLightRedColor,
- textColor: AppColors.primaryRedColor,
- fontSize: 14,
- fontWeight: FontWeight.bold,
- borderRadius: 12,
- padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
- height: 40.h,
+ children: [
+ (order.doctorName ?? "").toString().toText14(weight: FontWeight.w500),
+ SizedBox(height: 6.h),
+
+
+ ],
),
- ],
- ),
- ],
- ),
+ ),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ Row(children: [
+ CustomButton(
+ text: order.description!,
+ 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: 24.h,
+ )
+
+ ]
+ ),
+ SizedBox(height: 6.h),
+
+ Row(
+ children: [
+ CustomButton(
+ text: DateUtil.formatDateToDate(order.orderDate ?? order.appointmentDate ?? "", false),
+ 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: 24.h,
+ ),
+ SizedBox(width: 8.h),
+ CustomButton(
+ text: model.isSortByClinic ? (order.clinicDescription ?? "") : (order.projectName ?? ""),
+ 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: 24.h,
+ ),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ Row(
+ children: [
+ Expanded(
+ flex: 6,
+ child: SizedBox(),
+ ),
+ SizedBox(width: 8.h),
+ Expanded(
+ flex: 1,
+ child: Container(
+ height: 40.h,
+ width: 40.w,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.textColor,
+ borderRadius: 12,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(12.h),
+ child: Transform.flip(
+ flipX: false,
+ child: Utils.buildSvgWithAssets(
+ icon: AppAssets.forward_arrow_icon_small,
+ iconColor: AppColors.whiteColor,
+ fit: BoxFit.contain,
+ ),
+ ),
+ ),
+ ).onPress(() {
+ model.navigationService.push(
+ CustomPageRoute(
+ page: RadiologyResultPage(patientRadiologyResponseModel: order),
+ ),
+ );
+ }),
+ ),
+ ],
+ ),
+ ],
),
- crossFadeState: isExpanded ? CrossFadeState.showSecond : CrossFadeState.showFirst,
- duration: Duration(milliseconds: 300),
),
- ],
- ),
- ),
- ),
+ );
+ }).toList(),
+ ),
+ )
+ : SizedBox.shrink(),
),
- ),
- )
- : Utils.getNoDataWidget(context, noDataText: "You don't have any radiology results yet.".needTranslation);
- },
- ),
- ],
- ),
- );
- },
+ ],
+ ),
+ ),
+ ),
+ );
+ },
+ );
+ }),
+ ],
+ ),
+ );
+ },
+ ),
),
),
- ),
- );
+ );
}
Color getLabOrderStatusColor(num status) {