pull/206/head
haroon amjad 2 days ago
parent 1f290c5bfe
commit edeae70aa0

@ -1645,6 +1645,7 @@
"favouriteList": "قائمة المفضلة", "favouriteList": "قائمة المفضلة",
"later": "لاحقاً", "later": "لاحقاً",
"cancelAppointmentConfirmMessage": "هل أنت متأكد من رغبتك في إلغاء هذا الموعد؟", "cancelAppointmentConfirmMessage": "هل أنت متأكد من رغبتك في إلغاء هذا الموعد؟",
"acknowledged": "معترف به" "acknowledged": "معترف به",
"searchLabResults": "بحث نتائج المختبر"
} }

@ -1637,5 +1637,6 @@
"favouriteList": "Favourite List", "favouriteList": "Favourite List",
"later": "Later", "later": "Later",
"cancelAppointmentConfirmMessage": "Are you sure you want to cancel this appointment?", "cancelAppointmentConfirmMessage": "Are you sure you want to cancel this appointment?",
"acknowledged": "Acknowledged" "acknowledged": "Acknowledged",
"searchLabResults": "Search lab results"
} }

@ -516,7 +516,7 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
List<PatientAppointmentHistoryResponseModel> appointmentsList = List<PatientAppointmentHistoryResponseModel> appointmentsList =
list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientAppointmentHistoryResponseModel>(); list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientAppointmentHistoryResponseModel>();
appointmentsList.removeWhere((element) => element.isActiveDoctorProfile == false); // appointmentsList.removeWhere((element) => element.isActiveDoctorProfile == false);
apiResponse = GenericApiModel<List<PatientAppointmentHistoryResponseModel>>( apiResponse = GenericApiModel<List<PatientAppointmentHistoryResponseModel>>(
messageStatus: messageStatus, messageStatus: messageStatus,

@ -7,13 +7,15 @@ class NotificationsViewModel extends ChangeNotifier {
bool isNotificationsLoading = false; bool isNotificationsLoading = false;
bool hasMoreNotifications = true; bool hasMoreNotifications = true;
int unreadNotificationsCount = 0;
NotificationsRepo notificationsRepo; NotificationsRepo notificationsRepo;
ErrorHandlerService errorHandlerService; ErrorHandlerService errorHandlerService;
List<NotificationResponseModel> notificationsList = []; List<NotificationResponseModel> notificationsList = [];
int currentPage = 0; int currentPage = 0;
int pagingSize = 14; int pagingSize = 15;
int notificationStatusID = 2; // Default to status 2 (e.g., unread) int notificationStatusID = 2; // Default to status 2 (e.g., unread)
NotificationsViewModel({ NotificationsViewModel({
@ -46,6 +48,7 @@ class NotificationsViewModel extends ChangeNotifier {
Function(String)? onError, Function(String)? onError,
}) async { }) async {
isNotificationsLoading = true; isNotificationsLoading = true;
unreadNotificationsCount = 0;
notifyListeners(); notifyListeners();
final result = await notificationsRepo.getAllNotifications( final result = await notificationsRepo.getAllNotifications(
@ -78,6 +81,11 @@ class NotificationsViewModel extends ChangeNotifier {
} }
notificationsList.addAll(newNotifications); notificationsList.addAll(newNotifications);
for (var notification in notificationsList) {
if (notification.isRead == false) {
unreadNotificationsCount++;
}
}
currentPage++; currentPage++;
notifyListeners(); notifyListeners();

@ -1638,5 +1638,6 @@ abstract class LocaleKeys {
static const later = 'later'; static const later = 'later';
static const cancelAppointmentConfirmMessage = 'cancelAppointmentConfirmMessage'; static const cancelAppointmentConfirmMessage = 'cancelAppointmentConfirmMessage';
static const acknowledged = 'acknowledged'; static const acknowledged = 'acknowledged';
static const searchLabResults = 'searchLabResults';
} }

@ -162,6 +162,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
onConfirmTap: () async { onConfirmTap: () async {
Navigator.of(context).pop(); Navigator.of(context).pop();
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.cancellingAppointmentPleaseWait.tr(context: context)); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.cancellingAppointmentPleaseWait.tr(context: context));
myAppointmentsViewModel.onTabChange(0);
myAppointmentsViewModel.updateListWRTTab(0);
await myAppointmentsViewModel.cancelAppointment( await myAppointmentsViewModel.cancelAppointment(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onSuccess: (apiResponse) async { onSuccess: (apiResponse) async {

@ -55,13 +55,15 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false); bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView( body: Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
return CollapsingListView(
title: LocaleKeys.appointmentsList.tr(context: context), title: LocaleKeys.appointmentsList.tr(context: context),
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
children: [ children: [
SizedBox(height: 16.h), SizedBox(height: 16.h),
CustomTabBar( CustomTabBar(
initialIndex: myAppointmentsVM.selectedTabIndex,
activeTextColor: Color(0xffED1C2B), activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [ tabs: [
@ -78,13 +80,15 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
context.read<DateRangeSelectorRangeViewModel>().flush(); context.read<DateRangeSelectorRangeViewModel>().flush();
}, },
).paddingSymmetrical(24.h, 0.h), ).paddingSymmetrical(24.h, 0.h),
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) { // Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
return getSelectedTabData(myAppointmentsVM.selectedTabIndex, myAppointmentsVM); // return
}), getSelectedTabData(myAppointmentsVM.selectedTabIndex, myAppointmentsVM),
// }),
], ],
), ),
), ),
), );
}),
); );
} }

@ -323,6 +323,7 @@ class _MyDoctorsPageState extends State<MyDoctorsPage> {
); );
}); });
}, },
isDisabled: doctor?.isActiveDoctorProfile == false,
backgroundColor: AppColors.secondaryLightRedColor, backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor, textColor: AppColors.primaryRedColor,

@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_liveca
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_doctors_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart' show RegionBottomSheetBody; import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart' show RegionBottomSheetBody;
import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart';
@ -150,7 +151,25 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (appState.isAuthenticated) ...[], if (appState.isAuthenticated) ...[],
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.recentVisits.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.w, 0.h), LocaleKeys.recentVisits.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.w, 0.h),
Row(
children: [
LocaleKeys.viewAll.tr(context: context).toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500),
SizedBox(width: 2.h),
Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h),
],
).paddingSymmetrical(24.h, 0.h).onPress(() {
Navigator.of(context).push(
CustomPageRoute(
page: MyDoctorsPage(),
),
);
}),
],
),
SizedBox(height: 16.h), SizedBox(height: 16.h),
SizedBox( SizedBox(
height: 110.h, height: 110.h,

@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.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/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.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/core/utils/utils.dart';
@ -58,7 +59,17 @@ class WelcomeWidget extends StatelessWidget {
children: [ children: [
Flexible(child: name.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1, height: 1, isEnglishOnly: true)), Flexible(child: name.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1, height: 1, isEnglishOnly: true)),
// Icon(Icons.keyboard_arrow_down, size: 20, color: AppColors.greyTextColor), // Icon(Icons.keyboard_arrow_down, size: 20, color: AppColors.greyTextColor),
Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, height: 22.h, width: 22.w) Transform.flip(
flipX: getIt.get<AppState>().isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.arrowRight,
iconColor: AppColors.blackColor,
width: 22.w,
height: 22.h,
fit: BoxFit.contain,
)
),
// Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, height: 22.h, width: 22.w)
], ],
), ),
], ],

File diff suppressed because one or more lines are too long

@ -78,8 +78,9 @@ class _SearchLabResultsContentState extends State<SearchLabResultsContent> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
TextInputWidget( TextInputWidget(
labelText: "Search lab results", fontFamily: "Poppins",
hintText: "Type test name", labelText: LocaleKeys.searchLabResults.tr(context: context),
hintText: "",
controller: searchEditingController, controller: searchEditingController,
isEnable: true, isEnable: true,
prefix: null, prefix: null,

@ -17,16 +17,37 @@ import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
class NotificationsListPage extends StatelessWidget { class NotificationsListPage extends StatefulWidget {
const NotificationsListPage({super.key}); const NotificationsListPage({super.key});
@override
State<NotificationsListPage> createState() => _NotificationsListPageState();
}
class _NotificationsListPageState extends State<NotificationsListPage> {
final GlobalKey _bottomKey = GlobalKey();
void _scrollToBottom() {
WidgetsBinding.instance.addPostFrameCallback((_) {
final ctx = _bottomKey.currentContext;
if (ctx != null) {
Scrollable.ensureVisible(
ctx,
duration: const Duration(milliseconds: 400),
curve: Curves.easeOut,
);
}
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CollapsingListView( return CollapsingListView(
title: LocaleKeys.notification.tr(context: context), title: LocaleKeys.notification.tr(context: context),
child: SingleChildScrollView(
child: Consumer<NotificationsViewModel>(builder: (context, notificationsVM, child) { child: Consumer<NotificationsViewModel>(builder: (context, notificationsVM, child) {
return Container( return Column(
children: [
Container(
margin: EdgeInsets.symmetric(vertical: 24.h), margin: EdgeInsets.symmetric(vertical: 24.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, color: AppColors.whiteColor,
@ -34,19 +55,22 @@ class NotificationsListPage extends StatelessWidget {
hasShadow: false, hasShadow: false,
), ),
child: ListView.builder( child: ListView.builder(
itemCount: notificationsVM.isNotificationsLoading ? 4 : notificationsVM.notificationsList.length, itemCount: notificationsVM.isNotificationsLoading
? 4
: notificationsVM.notificationsList.length + (notificationsVM.hasMoreNotifications ? 1 : 0),
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
shrinkWrap: true, shrinkWrap: true,
padding: EdgeInsetsGeometry.zero, padding: EdgeInsetsGeometry.zero,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return notificationsVM.isNotificationsLoading if (notificationsVM.isNotificationsLoading) {
? LabResultItemView( return LabResultItemView(
onTap: () {}, onTap: () {},
labOrder: null, labOrder: null,
index: index, index: index,
isLoading: true, isLoading: true,
) );
: AnimationConfiguration.staggeredList( }
return AnimationConfiguration.staggeredList(
position: index, position: index,
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 500),
child: SlideAnimation( child: SlideAnimation(
@ -187,9 +211,23 @@ class NotificationsListPage extends StatelessWidget {
), ),
); );
}).paddingSymmetrical(16.w, 0.h), }).paddingSymmetrical(16.w, 0.h),
).paddingSymmetrical(24.w, 0.h); ).paddingSymmetrical(24.w, 0.h),
SizedBox(height: 16.h),
SizedBox(
key: _bottomKey,
child: "Show more notifications".toText16(
color: AppColors.primaryRedColor,
isBold: true,
isUnderLine: true
),
).onPress(() async {
await notificationsVM.loadMoreNotifications();
_scrollToBottom();
}),
SizedBox(height: 24.h),
],
);
}), }),
),
); );
} }

@ -57,9 +57,9 @@ class _AppLanguageChangeState extends State<AppLanguageChange> {
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: Column( child: Column(
children: [ children: [
languageItem("English", "en"),
1.divider,
languageItem("العربية", "ar"), languageItem("العربية", "ar"),
1.divider,
languageItem("English", "en"),
], ],
), ),
), ),

@ -48,6 +48,14 @@ class CustomTabBarState extends State<CustomTabBar> {
super.initState(); super.initState();
} }
@override
void didUpdateWidget(covariant CustomTabBar oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.initialIndex != widget.initialIndex) {
selectedIndex = widget.initialIndex;
}
}
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();

@ -289,15 +289,7 @@ class TextInputWidget extends StatelessWidget {
} }
Widget _buildLabelText(Color? labelColor) { Widget _buildLabelText(Color? labelColor) {
return Text( return labelText.toText12(fontWeight: FontWeight.w500, color: labelColor ?? AppColors.inputLabelTextColor);
labelText,
style: TextStyle(
fontSize: 12.f,
fontWeight: FontWeight.w500,
color: labelColor ?? AppColors.inputLabelTextColor,
letterSpacing: -0,
),
);
} }
Widget _buildTextField(BuildContext context) { Widget _buildTextField(BuildContext context) {

Loading…
Cancel
Save