diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 3d10df4e..147025c8 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:developer'; import 'dart:io'; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -264,6 +265,11 @@ class ApiClientImp implements ApiClient { final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr(); onFailure(message, -1, failureType: ConnectivityFailure(message)); _analytics.errorTracking.log(endPoint, error: "SocketException: $e"); + + Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim())); + Clarity.setCustomTag('api_err_request', _sanitizeRequestBody(requestBody)); + Clarity.sendCustomEvent('api_failure'); + return; } on http.ClientException catch (e) { final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr(); @@ -274,6 +280,10 @@ class ApiClientImp implements ApiClient { final message = LocaleKeys.networkTimeout.tr(); onFailure(message, -1, failureType: ConnectivityFailure(message)); _analytics.errorTracking.log(endPoint, error: "TimeoutException"); + + Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim())); + Clarity.setCustomTag('api_err_request', _sanitizeRequestBody(requestBody)); + Clarity.sendCustomEvent('api_timeout_failure'); return; } catch (e) { final message = LocaleKeys.networkUnknownError.tr(); @@ -287,6 +297,11 @@ class ApiClientImp implements ApiClient { if (statusCode < 200 || statusCode >= 400) { onFailure(LocaleKeys.networkErrorWhileFetching.tr(), statusCode, failureType: StatusCodeFailure(LocaleKeys.networkErrorWhileFetching.tr())); logApiEndpointError(endPoint, LocaleKeys.networkErrorWhileFetching.tr(), statusCode); + + Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim())); + Clarity.setCustomTag('api_err_request', _sanitizeRequestBody(requestBody)); + Clarity.setCustomTag('api_err_status', statusCode.toString()); + Clarity.sendCustomEvent('api_status_code_failure'); } else { var parsed = json.decode(utf8.decode(response.bodyBytes)); if (isAllowAny) { @@ -388,6 +403,11 @@ class ApiClientImp implements ApiClient { ); logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode); } + + Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim())); + Clarity.setCustomTag('api_err_request', _sanitizeRequestBody(requestBody)); + Clarity.setCustomTag('api_err_status', statusCode.toString()); + Clarity.sendCustomEvent('api_message_status_failure'); } } else { if (parsed['SameClinicApptList'] != null) { @@ -666,4 +686,42 @@ class ApiClientImp implements ApiClient { logApiEndpointError(String endPoint, error, code) { _analytics.errorTracking.log(endPoint, error: error); } + + String _getEndofUrl(String url) { + const int clarityLimit = 255; + if (url.length <= clarityLimit) return url; + + // Extracts the final 252 characters and prepends '...' + return '...${url.substring(url.length - (clarityLimit - 3))}'; + } + + String _sanitizeRequestBody(String body) { + if (body.isEmpty) return ''; + + try { + // Handle standard JSON bodies + final Map data = json.decode(body); + + // Case-insensitive key matching and removal + data.removeWhere((key, value) => key.toLowerCase() == 'sessionid' || key.toLowerCase() == 'tokenid'); + + return json.encode(data); + } catch (_) { + // Fallback: Handle Form URL Encoded strings or raw key-value pairs (SessionID=XYZ&TokenID=abc) + String modifiedBody = body; + final sensitiveKeys = ['SessionID', 'TokenID']; + + for (var key in sensitiveKeys) { + // Matches "key=value" surrounded by standard query delimiters + final regExp = RegExp(r'' + key + r'=[^&]*&?', caseSensitive: false); + modifiedBody = modifiedBody.replaceAll(regExp, ''); + } + + // Clean up any trailing trailing ampersands + if (modifiedBody.endsWith('&')) { + modifiedBody = modifiedBody.substring(0, modifiedBody.length - 1); + } + return modifiedBody; + } + } } diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart index 65b44d7d..aae2d03b 100644 --- a/lib/core/app_state.dart +++ b/lib/core/app_state.dart @@ -49,8 +49,8 @@ class AppState { bool isPaytabsEnabled = false; void setIsPaytabsEnabled(bool value) { - // isPaytabsEnabled = value; - isPaytabsEnabled = false; + isPaytabsEnabled = value; + // isPaytabsEnabled = false; } int? _superUserID; diff --git a/lib/features/authentication/widgets/otp_verification_screen.dart b/lib/features/authentication/widgets/otp_verification_screen.dart index 00eb768f..372f7e8f 100644 --- a/lib/features/authentication/widgets/otp_verification_screen.dart +++ b/lib/features/authentication/widgets/otp_verification_screen.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -139,6 +140,7 @@ class OTPWidgetState extends State with SingleTickerProviderStateMixi @override void initState() { + Clarity.setCurrentScreenName('OTP Verification Screen'); super.initState(); authVm = context.read(); focusNode = widget.focusNode ?? FocusNode(); diff --git a/lib/features/paytabs/paytabs_view_model.dart b/lib/features/paytabs/paytabs_view_model.dart index 8615ca22..59e31903 100644 --- a/lib/features/paytabs/paytabs_view_model.dart +++ b/lib/features/paytabs/paytabs_view_model.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_paytabs_bridge/BaseBillingShippingInfo.dart'; import 'package:flutter_paytabs_bridge/IOSThemeConfiguration.dart'; +import 'package:flutter_paytabs_bridge/PaymentSDKNetworks.dart'; import 'package:flutter_paytabs_bridge/PaymentSdkConfigurationDetails.dart'; import 'package:flutter_paytabs_bridge/PaymentSdkLocale.dart'; import 'package:flutter_paytabs_bridge/PaymentSdkTokenFormat.dart'; diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index 47ddef09..0c5a418e 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'dart:ui' as ui; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; @@ -85,6 +86,7 @@ class _AppointmentDetailsPageState extends State { @override void initState() { + Clarity.setCurrentScreenName('Appointment Details Page'); super.initState(); scheduleMicrotask(() async { @@ -892,39 +894,36 @@ class _AppointmentDetailsPageState extends State { showCommonBottomSheetWithoutHeight( title: LocaleKeys.parkingQR.tr(context: context), context, - child: SizedBox( - // height: 150.h, - child: Column( - children: [ - LocaleKeys.parkingQRDesc.tr(context: context).toText16(), - SizedBox(height: 16.h), - Image.memory( - myAppointmentsViewModel.parkingQRImageBytes!, - width: 156.w, - height: 156.h, - fit: BoxFit.cover, - gaplessPlayback: true, // Prevents blink during image rebuild - ), - SizedBox(height: 16.h), - CustomButton( - text: LocaleKeys.done.tr(context: context), - onPressed: () { - openDoctorScheduleCalendar(); - }, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedColor, - textColor: Colors.white, - fontSize: 16.f, - fontWeight: FontWeight.w600, - borderRadius: 12.r, - padding: EdgeInsets.symmetric(horizontal: 10.w), - height: 50.h, - icon: AppAssets.checkmark_icon, - iconColor: Colors.white, - iconSize: 18.h, - ).paddingSymmetrical(0.h, 0.h), - ], - ), + child: Column( + children: [ + LocaleKeys.parkingQRDesc.tr(context: context).toText16(), + SizedBox(height: 16.h), + Image.memory( + myAppointmentsViewModel.parkingQRImageBytes!, + width: 156.w, + height: 156.h, + fit: BoxFit.cover, + gaplessPlayback: true, // Prevents blink during image rebuild + ), + SizedBox(height: 16.h), + CustomButton( + text: LocaleKeys.done.tr(context: context), + onPressed: () { + openDoctorScheduleCalendar(); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: Colors.white, + fontSize: 16.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 50.h, + icon: AppAssets.checkmark_icon, + iconColor: Colors.white, + iconSize: 18.h, + ).paddingSymmetrical(0.h, 0.h), + ], ), callBackFunc: () {}, isFullScreen: false, @@ -1312,6 +1311,7 @@ class _AppointmentDetailsPageState extends State { case 50: // return LocaleKeys.confirmLiveCare.tr(context: context); case 90: + Clarity.setCurrentScreenName('Appointment CheckIn Page'); showCommonBottomSheetWithoutHeight(context, title: LocaleKeys.onlineCheckIn.tr(context: context), child: isAppointmentWithin4Hours @@ -1321,9 +1321,9 @@ class _AppointmentDetailsPageState extends State { ) : Utils.getOnlineCheckInWidget( loadingText: LocaleKeys.onlineCheckIn24HoursOnly.tr(context: context), - ), - callBackFunc: () {}, - isFullScreen: false); + ), callBackFunc: () { + Clarity.setCurrentScreenName('Appointment Details Page'); + }, isFullScreen: false); default: // return "No Action".needTranslation; } diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index bae7514c..36e386b9 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:developer'; import 'dart:io'; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -59,6 +60,7 @@ class _AppointmentPaymentPageState extends State { @override void initState() { scheduleMicrotask(() { + Clarity.setCurrentScreenName('Appointment Payment Page'); payfortViewModel.initPayfortViewModel(); payfortViewModel.setIsApplePayConfigurationLoading(false); myAppointmentsViewModel.getPatientShareAppointment( @@ -443,21 +445,24 @@ class _AppointmentPaymentPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ LocaleKeys.amountBeforeTax.tr(context: context).toText14(isBold: true), - Utils.getPaymentAmountWithSymbol((myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare ?? 0).toString().toText16(isBold: true), AppColors.blackColor, - 13, - isSaudiCurrency: true), + ClarityUnmask( + child: Utils.getPaymentAmountWithSymbol( + (myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare ?? 0).toString().toText16(isBold: true), AppColors.blackColor, 13, + isSaudiCurrency: true), + ), ], ).paddingSymmetrical(24.h, 0.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ "VAT 15%".toText14(isBold: true, color: AppColors.greyTextColor), - Utils.getPaymentAmountWithSymbol( - (myAppointmentsVM.patientAppointmentShareResponseModel!.patientTaxAmount ?? 0).toString() - .toText14(isBold: true, color: AppColors.greyTextColor), - AppColors.greyTextColor, - 13, - isSaudiCurrency: true), + ClarityUnmask( + child: Utils.getPaymentAmountWithSymbol( + (myAppointmentsVM.patientAppointmentShareResponseModel!.patientTaxAmount ?? 0).toString().toText14(isBold: true, color: AppColors.greyTextColor), + AppColors.greyTextColor, + 13, + isSaudiCurrency: true), + ), ], ).paddingSymmetrical(24.h, 0.h), SizedBox(height: 17.h), @@ -465,10 +470,11 @@ class _AppointmentPaymentPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ "".toText14(isBold: true), - Utils.getPaymentAmountWithSymbol( - (myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax ?? 0).toString().toText24(isBold: true), AppColors.blackColor, - 17, - isSaudiCurrency: true), + ClarityUnmask( + child: Utils.getPaymentAmountWithSymbol( + (myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax ?? 0).toString().toText24(isBold: true), AppColors.blackColor, 17, + isSaudiCurrency: true), + ), ], ).paddingSymmetrical(24.h, 0.h), Platform.isIOS diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index 03c12c3b..9cb5906e 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:ui' as ui; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -309,16 +310,18 @@ class _AppointmentCardState extends State { ? '${(widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital").substring(0, 15)}...' : widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital") .toShimmer2(isShow: widget.isLoading), - Directionality( - textDirection: ui.TextDirection.ltr, - child: AppCustomChipWidget( - labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), - icon: AppAssets.appointment_calendar_icon, - richText: widget.isLoading - ? 'Cardiology'.toText10().toShimmer2(isShow: widget.isLoading) - : "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)}" - .toText10(isEnglishOnly: true, isBold: true), - ).toShimmer2(isShow: widget.isLoading), + ClarityUnmask( + child: Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), + icon: AppAssets.appointment_calendar_icon, + richText: widget.isLoading + ? 'Cardiology'.toText10().toShimmer2(isShow: widget.isLoading) + : "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)}" + .toText10(isEnglishOnly: true, isBold: true), + ).toShimmer2(isShow: widget.isLoading), + ), ), // AppCustomChipWidget( diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart index 299c3db9..81958507 100644 --- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart +++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart @@ -1,5 +1,6 @@ import 'dart:ui' as ui; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -134,17 +135,18 @@ class AppointmentDoctorCard extends StatelessWidget { labelText: patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital", labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w), ), - Directionality( - textDirection: ui.TextDirection.ltr, - child: AppCustomChipWidget( - labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w), - icon: AppAssets.doctor_calendar_icon, - richText: - "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang( - DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), - false, - )}" - .toText10(isBold: true), + ClarityUnmask( + child: Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w), + icon: AppAssets.doctor_calendar_icon, + richText: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang( + DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), + false, + )}" + .toText10(isBold: true), + ), ), ), AppCustomChipWidget( diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart index 21f46bea..371ad6ab 100644 --- a/lib/presentation/authentication/login.dart +++ b/lib/presentation/authentication/login.dart @@ -1,3 +1,4 @@ +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -32,6 +33,7 @@ class LoginScreenState extends State { @override void initState() { + Clarity.setCurrentScreenName('Login Screen'); super.initState(); _nationalIdFocusNode = FocusNode(); diff --git a/lib/presentation/authentication/saved_login_screen.dart b/lib/presentation/authentication/saved_login_screen.dart index 7f0cdc34..19080ae8 100644 --- a/lib/presentation/authentication/saved_login_screen.dart +++ b/lib/presentation/authentication/saved_login_screen.dart @@ -1,5 +1,6 @@ import 'dart:ui' as ui; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -37,6 +38,7 @@ class _SavedLogin extends State { @override void initState() { + Clarity.setCurrentScreenName('Last Login Screen'); authVm = context.read(); appState = getIt.get(); loginType = LoginTypeExtension.fromValue(appState.getSelectDeviceByImeiRespModelElement!.logInType!)!; @@ -58,7 +60,6 @@ class _SavedLogin extends State { if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) { authVm.loginWithFingerPrintFace(() {}); } - super.initState(); } diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index 00a0e5a2..074929a1 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; @@ -57,6 +58,7 @@ class _BookAppointmentPageState extends State { @override void initState() { + Clarity.setCurrentScreenName('Book Appointment Page'); authVM = context.read(); scheduleMicrotask(() { // bookAppointmentsViewModel.selectedTabIndex = 0; diff --git a/lib/presentation/book_appointment/review_appointment_page.dart b/lib/presentation/book_appointment/review_appointment_page.dart index 13a964a6..697ec837 100644 --- a/lib/presentation/book_appointment/review_appointment_page.dart +++ b/lib/presentation/book_appointment/review_appointment_page.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:developer'; import 'dart:typed_data'; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; @@ -48,6 +49,12 @@ class _ReviewAppointmentPageState extends State { Uint8List? _cachedImageBytes; String? _cachedImageDataHash; + @override + void initState() { + Clarity.setCurrentScreenName('Appointment Review Page'); + super.initState(); + } + @override Widget build(BuildContext context) { bookAppointmentsViewModel = Provider.of(context, listen: false); diff --git a/lib/presentation/book_appointment/search_doctor_by_name.dart b/lib/presentation/book_appointment/search_doctor_by_name.dart index 3222fbd8..438cd746 100644 --- a/lib/presentation/book_appointment/search_doctor_by_name.dart +++ b/lib/presentation/book_appointment/search_doctor_by_name.dart @@ -1,3 +1,4 @@ +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; @@ -39,6 +40,7 @@ class _SearchDoctorByNameState extends State { @override void initState() { + Clarity.setCurrentScreenName('Search By Doctor Name Page'); _scrollController = ScrollController(); super.initState(); } diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart index 05abdb8a..cf1cd897 100644 --- a/lib/presentation/book_appointment/select_clinic_page.dart +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; @@ -59,6 +60,7 @@ class _SelectClinicPageState extends State { @override void initState() { + Clarity.setCurrentScreenName('Select Clinic Page'); scheduleMicrotask(() { bookAppointmentsViewModel.getClinics(); }); diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart index d9012573..26ab5519 100644 --- a/lib/presentation/book_appointment/select_doctor_page.dart +++ b/lib/presentation/book_appointment/select_doctor_page.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; @@ -44,6 +45,7 @@ class _SelectDoctorPageState extends State { @override void initState() { + Clarity.setCurrentScreenName('Select Doctor Page'); _scrollController = ScrollController(); scheduleMicrotask(() { bookAppointmentsViewModel.setIsNearestAppointmentSelected(true); diff --git a/lib/presentation/book_appointment/widgets/appointment_calendar.dart b/lib/presentation/book_appointment/widgets/appointment_calendar.dart index 544b55c8..ea954e03 100644 --- a/lib/presentation/book_appointment/widgets/appointment_calendar.dart +++ b/lib/presentation/book_appointment/widgets/appointment_calendar.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:ui' as ui; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -59,6 +60,7 @@ class _AppointmentCalendarState extends State { @override void initState() { + Clarity.setCurrentScreenName('Select Doctor Free Slot Page'); _calendarController = CalendarController(); scheduleMicrotask(() { _events = { @@ -98,54 +100,56 @@ class _AppointmentCalendarState extends State { child: Localizations.override( context: context, locale: isArabic ? const Locale('ar', 'SA') : const Locale('en'), - child: SfCalendar( - controller: _calendarController, - minDate: DateTime.now(), - showNavigationArrow: true, - // headerHeight: 60.h, - headerStyle: CalendarHeaderStyle( - backgroundColor: AppColors.transparent, - textAlign: isArabic ? TextAlign.end : TextAlign.start, - textStyle: TextStyle(fontSize: 18.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.primaryRedColor), - ), - viewHeaderStyle: ViewHeaderStyle( - // backgroundColor: AppColors.scaffoldBgColor, - dayTextStyle: TextStyle(fontSize: 14.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.textColor), - ), - view: CalendarView.month, - todayHighlightColor: Colors.transparent, - todayTextStyle: TextStyle(color: AppColors.textColor, fontWeight: FontWeight.bold), - selectionDecoration: ShapeDecoration( - color: AppColors.transparent, - shape: SmoothRectangleBorder( - borderRadius: BorderRadius.circular(10.r), - smoothness: 1, - side: BorderSide(color: AppColors.primaryRedColor, width: 1.5), + child: ClarityUnmask( + child: SfCalendar( + controller: _calendarController, + minDate: DateTime.now(), + showNavigationArrow: true, + // headerHeight: 60.h, + headerStyle: CalendarHeaderStyle( + backgroundColor: AppColors.transparent, + textAlign: isArabic ? TextAlign.end : TextAlign.start, + textStyle: TextStyle(fontSize: 18.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.primaryRedColor), ), - ), - cellBorderColor: AppColors.transparent, - dataSource: MeetingDataSource(_getDataSource()), - monthCellBuilder: (context, details) => Padding( - padding: EdgeInsets.all(12.h), - child: details.date.day.toString().toText14( - isCenter: true, - color: details.date == _calendarController.selectedDate ? AppColors.primaryRedColor : AppColors.textColor, - isEnglishOnly: true, - ), - ), - monthViewSettings: MonthViewSettings( - dayFormat: "EEE", - appointmentDisplayMode: MonthAppointmentDisplayMode.indicator, - showTrailingAndLeadingDates: false, - appointmentDisplayCount: 1, - monthCellStyle: MonthCellStyle( - textStyle: TextStyle(fontSize: 19.f), + viewHeaderStyle: ViewHeaderStyle( + // backgroundColor: AppColors.scaffoldBgColor, + dayTextStyle: TextStyle(fontSize: 14.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.textColor), + ), + view: CalendarView.month, + todayHighlightColor: Colors.transparent, + todayTextStyle: TextStyle(color: AppColors.textColor, fontWeight: FontWeight.bold), + selectionDecoration: ShapeDecoration( + color: AppColors.transparent, + shape: SmoothRectangleBorder( + borderRadius: BorderRadius.circular(10.r), + smoothness: 1, + side: BorderSide(color: AppColors.primaryRedColor, width: 1.5), + ), ), + cellBorderColor: AppColors.transparent, + dataSource: MeetingDataSource(_getDataSource()), + monthCellBuilder: (context, details) => Padding( + padding: EdgeInsets.all(12.h), + child: details.date.day.toString().toText14( + isCenter: true, + color: details.date == _calendarController.selectedDate ? AppColors.primaryRedColor : AppColors.textColor, + isEnglishOnly: true, + ), + ), + monthViewSettings: MonthViewSettings( + dayFormat: "EEE", + appointmentDisplayMode: MonthAppointmentDisplayMode.indicator, + showTrailingAndLeadingDates: false, + appointmentDisplayCount: 1, + monthCellStyle: MonthCellStyle( + textStyle: TextStyle(fontSize: 19.f), + ), + ), + onTap: (CalendarTapDetails details) { + _calendarController.selectedDate = details.date; + _onDaySelected(details.date!); + }, ), - onTap: (CalendarTapDetails details) { - _calendarController.selectedDate = details.date; - _onDaySelected(details.date!); - }, ), ), ), @@ -153,35 +157,37 @@ class _AppointmentCalendarState extends State { SizedBox(height: 10.h), Transform.translate( offset: const Offset(0.0, -10.0), - child: selectedDateDisplay.toText16(isBold: true, isEnglishOnly: !isArabic), + child: ClarityUnmask(child: selectedDateDisplay.toText16(isBold: true, isEnglishOnly: !isArabic)), ), //TODO: Add Next Day Span here dayEvents.isNotEmpty - ? ConstrainedBox( - constraints: BoxConstraints( - maxHeight: MediaQuery.of(context).size.height * 0.15, - minHeight: 0, - ), - child: Directionality( - textDirection: isArabic ? ui.TextDirection.rtl : ui.TextDirection.ltr, - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Wrap( - direction: Axis.horizontal, - alignment: WrapAlignment.start, - spacing: 6.h, - runSpacing: 6.h, - children: List.generate( - dayEvents.length, - (index) => TimeSlotChip( - label: dayEvents[index].isoTime!, - isSelected: index == selectedButtonIndex, - onTap: () { - setState(() { - selectedButtonIndex = index; - selectedTime = dayEvents[index].isoTime!; - }); - }, + ? ClarityUnmask( + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 0.15, + minHeight: 0, + ), + child: Directionality( + textDirection: isArabic ? ui.TextDirection.rtl : ui.TextDirection.ltr, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.start, + spacing: 6.h, + runSpacing: 6.h, + children: List.generate( + dayEvents.length, + (index) => TimeSlotChip( + label: dayEvents[index].isoTime!, + isSelected: index == selectedButtonIndex, + onTap: () { + setState(() { + selectedButtonIndex = index; + selectedTime = dayEvents[index].isoTime!; + }); + }, + ), ), ), ), diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 40028315..1e48083c 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:developer'; import 'dart:ui' as ui; +import 'package:clarity_flutter/clarity_flutter.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; @@ -112,6 +113,7 @@ class _LandingPageState extends State { @override void initState() { + Clarity.setCurrentScreenName('Landing Page'); authVM = context.read(); habibWalletVM = context.read(); appointmentRatingViewModel = context.read(); diff --git a/lib/splashPage.dart b/lib/splashPage.dart index a93f75a1..2edf222e 100644 --- a/lib/splashPage.dart +++ b/lib/splashPage.dart @@ -60,9 +60,9 @@ class _SplashScreenState extends State { ZoomService().initializeZoomSDK(); - // if (!kDebugMode) { - // _initializeClarity(); - // } + if (!kDebugMode) { + _initializeClarity(); + } if (isAppOpenedFromCall) { navigateToTeleConsult(); @@ -87,7 +87,7 @@ class _SplashScreenState extends State { void _initializeClarity() { final config = ClarityConfig( projectId: "x0qgorlez4", // You can find it on the Settings page of Clarity dashboard. - logLevel: LogLevel.Verbose, // Optional: Set the log level (Verbose, Debug, Info, Warning, Error, None) + logLevel: LogLevel.Error, // Optional: Set the log level (Verbose, Debug, Info, Warning, Error, None) ); Clarity.initialize(context, config);