diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 2fd29149..f350b9b9 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -1443,8 +1443,8 @@ "pleaseSelectAtLeastOneOptionBeforeProceeding": "يرجى اختيار خيار واحد على الأقل قبل المتابعة", "suggestions": "التوصيات", "pleaseGoBackAndSelectOrgansFirst": "يرجى العودة واختيار الأعضاء أولاً", - "symptomsSelector": "محدد الأعراض", - "possibleSymptomsRelatedTo": "أعراض مرتبطة بـ", + "symptomsSelector": "محدد الأعراض", + "possibleSymptomsRelatedTo": "الأعراض المحتملة المتعلقة بأعضاء محددة", "suggestionAIListDescription": "هذه قائمة بالأعراض المقترحة من قبل الذكاء الاصطناعي، بناءً على المعلومات التي تم جمعها حتى الآن خلال المقابلة", "whyAmIBeingAskedThis": "لماذا يُطرح علي هذا السؤال؟", "weAreAskingThisQuestionToBetterUnderstand": "نطرح هذا السؤال لفهم {symptoms} بشكل أفضل وتحسين دقة التقييم.", @@ -1856,6 +1856,7 @@ "watchUsOnYoutube": "شاهدنا على يوتيوب", "connectOnLinkedin": "تواصل معنا على لينكد إن", "riskFactorPageTitle": "اعراض تشعر بها", + "doAnyOfTheseApply": "هل ينطبق عليك أي من هذه الأمور؟", "bloodDonationService": "التبرع \n بالدم", "bookAppointmentService": "حجز\nموعد", "pinchToZoom": "اضغط للتكبير", diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 43ff1c11..83e93b3f 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -1436,7 +1436,7 @@ "suggestions": "Suggestions", "pleaseGoBackAndSelectOrgansFirst": "Please go back and select organs first", "symptomsSelector": "Symptoms Selector", - "possibleSymptomsRelatedTo": "Possible symptoms related to", + "possibleSymptomsRelatedTo": "Possible symptoms related to selected organs", "suggestionAIListDescription": "This is a list of symptoms suggested by our AI, based on the information gathered so far during the interview", "whyAmIBeingAskedThis": "Why am I being asked this?", "weAreAskingThisQuestionToBetterUnderstand": "We are asking this question to better understand your {symptoms} and improve the accuracy of the assessment.", @@ -1848,6 +1848,7 @@ "watchUsOnYoutube": "Watch us on youtube", "connectOnLinkedin": "Connect on LinkedIn", "riskFactorPageTitle": "What do you feel?", + "doAnyOfTheseApply": "Do any of these apply to you?", "bloodDonationService": "Blood \nDonation", "bookAppointmentService": "Book\nAppointment", "pinchToZoom": "Pinch to zoom", diff --git a/lib/features/symptoms_checker/models/resp_models/triage_response_model.dart b/lib/features/symptoms_checker/models/resp_models/triage_response_model.dart index 60b748b1..e8992014 100644 --- a/lib/features/symptoms_checker/models/resp_models/triage_response_model.dart +++ b/lib/features/symptoms_checker/models/resp_models/triage_response_model.dart @@ -105,20 +105,14 @@ class TriageQuestion { factory TriageQuestion.fromJson(Map json) { // TESTING: Add dummy instructions to every question for testing UI - final dummyInstructions = [ - "Clean the thermometer using soap and cold water.", - "For rectal insertion, apply a small amount of petroleum jelly to the end of the thermometer.", - "Insert the silver tip into the anus or mouth.", - "Hold the thermometer in place for 2 minutes, or as long as recommended by the manufacturer.", - ]; - - final dummyExplication = "The most accurate measurement is rectal temperature. You can also measure the temperature under the armpit or use contactless thermometers. However, using the contact thermometer is more accurate. Follow the thermometer manufacturer's instructions. A temperature above 37°C or 98.6°F is classified as a fever."; return TriageQuestion( type: json['type'], text: json['text'], - explication: json['explication'] ?? dummyExplication, // Use dummy if null - instruction: json['instruction'] != null ? List.from(json['instruction']) : dummyInstructions, // Use dummy if null + explication: json['explication'], + // Use dummy if null + instruction: json['instruction'] != null ? List.from(json['instruction']) : [], + // Use dummy if null items: json['items'] != null ? (json['items'] as List).map((item) => TriageQuestionItem.fromJson(item)).toList() : null, ); } diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 4c449f3c..71fb165d 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -2,7 +2,7 @@ // ignore_for_file: constant_identifier_names -abstract class LocaleKeys { +abstract class LocaleKeys { static const english = 'english'; static const arabic = 'arabic'; static const login = 'login'; @@ -1808,6 +1808,7 @@ abstract class LocaleKeys { static const watchUsOnYoutube = 'watchUsOnYoutube'; static const connectOnLinkedin = 'connectOnLinkedin'; static const riskFactorPageTitle = 'riskFactorPageTitle'; + static const doAnyOfTheseApply = 'doAnyOfTheseApply'; static const bloodDonationService = 'bloodDonationService'; static const bookAppointmentService = 'bookAppointmentService'; static const pinchToZoom = 'pinchToZoom'; @@ -1883,5 +1884,4 @@ abstract class LocaleKeys { static const advanceNumber = 'advanceNumber'; static const amountOnly = 'amountOnly'; static const invoiceHistory = 'invoiceHistory'; - } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 51e27211..0e6de0af 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1847,6 +1847,7 @@ abstract class LocaleKeys { static const watchUsOnYoutube = 'watchUsOnYoutube'; static const connectOnLinkedin = 'connectOnLinkedin'; static const riskFactorPageTitle = 'riskFactorPageTitle'; + static const doAnyOfTheseApply = 'doAnyOfTheseApply'; static const bloodDonationService = 'bloodDonationService'; static const bookAppointmentService = 'bookAppointmentService'; static const pinchToZoom = 'pinchToZoom'; diff --git a/lib/presentation/symptoms_checker/organ_selector_screen.dart b/lib/presentation/symptoms_checker/organ_selector_screen.dart index 840a24b6..2244ecbd 100644 --- a/lib/presentation/symptoms_checker/organ_selector_screen.dart +++ b/lib/presentation/symptoms_checker/organ_selector_screen.dart @@ -71,16 +71,21 @@ class _OrganSelectorPageState extends State { ); return; } + + // If user is not authenticated, navigate to user consent screen + if (!_appState.isAuthenticated) { + context.navigateWithName(AppRoutes.userConsentScreen); + return; + } + + // User is authenticated, proceed with API call LoaderBottomSheet.showLoader( loadingText: LocaleKeys.pleaseWait.tr(context: context), ); - // Get fileNo if user is logged in - String? fileNo; - if (_appState.isAuthenticated) { - final user = _appState.getAuthenticatedUser(); - fileNo = user?.patientId.toString(); - } + // Get fileNo from authenticated user + final user = _appState.getAuthenticatedUser(); + String? fileNo = user?.patientId.toString(); await viewModel.getSymptomsUserDetails( userName: ApiConsts.symptomsCheckerUsername, diff --git a/lib/presentation/symptoms_checker/possible_conditions_screen.dart b/lib/presentation/symptoms_checker/possible_conditions_screen.dart index b8807b83..f7740602 100644 --- a/lib/presentation/symptoms_checker/possible_conditions_screen.dart +++ b/lib/presentation/symptoms_checker/possible_conditions_screen.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -31,7 +29,6 @@ import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:provider/provider.dart'; -import 'package:shimmer/shimmer.dart'; class PossibleConditionsPage extends StatefulWidget { const PossibleConditionsPage({super.key}); @@ -91,25 +88,81 @@ class _PossibleConditionsPageState extends State { } Widget _buildLoadingShimmer() { - return ListView.separated( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - padding: EdgeInsets.all(16.w), - itemCount: 10, - separatorBuilder: (_, __) => SizedBox(height: 12.h), - itemBuilder: (context, index) { - return Shimmer.fromColors( - baseColor: Colors.grey[300]!, - highlightColor: Colors.grey[100]!, - child: Container( - height: 80.h, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Triage Level Card Shimmer + + // Condition Cards Shimmer + ...List.generate(5, (index) { + return Container( + width: double.infinity, + margin: EdgeInsets.only(left: 24.w, right: 24.w, bottom: 16.h), + padding: EdgeInsets.all(20.w), decoration: BoxDecoration( color: AppColors.whiteColor, - borderRadius: BorderRadius.circular(10.r), + borderRadius: BorderRadius.circular(24.r), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.08), + blurRadius: 10.r, + offset: Offset(0, 2.h), + ), + ], ), - ), - ); - }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Condition name shimmer + Container( + height: 20.h, + width: 220.w, + decoration: BoxDecoration( + color: AppColors.whiteColor, + borderRadius: BorderRadius.circular(8.r), + ), + ).toShimmer2(isShow: true, radius: 8.r), + SizedBox(height: 8.h), + + Container( + height: 20.h, + width: 200.w, + decoration: BoxDecoration( + color: AppColors.whiteColor, + borderRadius: BorderRadius.circular(8.r), + ), + ).toShimmer2(isShow: true, radius: 8.r), + SizedBox(height: 8.h), + + // Probability percentage shimmer + Container( + height: 16.h, + width: 100.w, + decoration: BoxDecoration( + color: AppColors.whiteColor, + borderRadius: BorderRadius.circular(8.r), + ), + ).toShimmer2(isShow: true, radius: 8.r), + SizedBox(height: 30.h), + // Action buttons shimmer + Row( + children: [ + Expanded( + child: Container( + height: 40.h, + decoration: BoxDecoration( + color: AppColors.whiteColor, + borderRadius: BorderRadius.circular(12.r), + ), + ).toShimmer2(isShow: true, radius: 12.r), + ), + ], + ), + ], + ), + ); + }), + ], ); } @@ -323,7 +376,10 @@ class _PossibleConditionsPageState extends State { trailing: _buildTrailingSection(context), child: Consumer( builder: (context, symptomsCheckerViewModel, child) { - if (symptomsCheckerViewModel.isPossibleConditionsLoading || symptomsCheckerViewModel.isTriageDiagnosisLoading) { + // Show shimmer while loading triage level or conditions + if (symptomsCheckerViewModel.isPossibleConditionsLoading || + symptomsCheckerViewModel.isTriageDiagnosisLoading || + symptomsCheckerViewModel.isTriageLevelLoading) { return _buildLoadingShimmer(); } // Get conditions directly from ViewModel @@ -350,9 +406,7 @@ class _PossibleConditionsPageState extends State { // Show error if no clinic details available final dialogService = getIt(); dialogService.showErrorBottomSheet( - message: appState.isArabic() - ? "لا توجد تفاصيل عيادة متاحة" - : "No clinic details available", + message: appState.isArabic() ? "لا توجد تفاصيل عيادة متاحة" : "No clinic details available", ); return; } @@ -451,13 +505,12 @@ class _PossibleConditionsPageState extends State { var id = ""; if (data.regionBottomSheetType == RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER) { // Check if hospitalList is not empty before accessing first - if (regionalViewModel.selectedHospital?.hospitalList != null && - regionalViewModel.selectedHospital!.hospitalList.isNotEmpty) { + if (regionalViewModel.selectedHospital?.hospitalList != null && regionalViewModel.selectedHospital!.hospitalList.isNotEmpty) { id = regionalViewModel.selectedHospital!.hospitalList.first.iD?.toString() ?? ""; } } else { // Check if patientDoctorAppointmentList is not empty before accessing first - if (regionalViewModel.selectedHospital?.patientDoctorAppointmentList != null && + if (regionalViewModel.selectedHospital?.patientDoctorAppointmentList != null && regionalViewModel.selectedHospital!.patientDoctorAppointmentList!.isNotEmpty) { id = regionalViewModel.selectedHospital!.patientDoctorAppointmentList!.first.projectID?.toString() ?? ""; } diff --git a/lib/presentation/symptoms_checker/risk_factors_screen.dart b/lib/presentation/symptoms_checker/risk_factors_screen.dart index 08e7abdd..0e150733 100644 --- a/lib/presentation/symptoms_checker/risk_factors_screen.dart +++ b/lib/presentation/symptoms_checker/risk_factors_screen.dart @@ -1,5 +1,4 @@ import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; @@ -121,12 +120,11 @@ class _RiskFactorsScreenState extends State { child: RichText( text: TextSpan( style: TextStyle( - height: 1.3, - fontSize: 13.f, + height: 1.3, + fontSize: 13.f, fontWeight: FontWeight.w600, - color: AppColors.greyInfoTextColor, - fontFamily: isArabic ? 'CairoArabic' : 'Poppins' - ), + color: AppColors.greyInfoTextColor, + fontFamily: isArabic ? 'CairoArabic' : 'Poppins'), children: [ TextSpan( text: LocaleKeys.aboveYouSeeCommonRiskFactors.tr(context: context), @@ -223,7 +221,7 @@ class _RiskFactorsScreenState extends State { children: [ Expanded( child: CollapsingListView( - title: LocaleKeys.riskFactorPageTitle.tr(context: context), + title: LocaleKeys.doAnyOfTheseApply.tr(context: context), leadingCallback: () => context.pop(), child: viewModel.isRiskFactorsLoading ? _buildLoadingShimmer() diff --git a/lib/presentation/symptoms_checker/suggestions_screen.dart b/lib/presentation/symptoms_checker/suggestions_screen.dart index dddb62db..5e102581 100644 --- a/lib/presentation/symptoms_checker/suggestions_screen.dart +++ b/lib/presentation/symptoms_checker/suggestions_screen.dart @@ -200,7 +200,7 @@ class _SuggestionsScreenState extends State { children: [ Expanded( child: CollapsingListView( - title: LocaleKeys.symptomsCheckSuggestions.tr() , + title: LocaleKeys.riskFactorPageTitle.tr() , leadingCallback: () => context.pop(), child: viewModel.isSuggestionsLoading ? _buildLoadingShimmer() diff --git a/lib/presentation/symptoms_checker/symptoms_selector_screen.dart b/lib/presentation/symptoms_checker/symptoms_selector_screen.dart index 9f9ac86e..6a83316b 100644 --- a/lib/presentation/symptoms_checker/symptoms_selector_screen.dart +++ b/lib/presentation/symptoms_checker/symptoms_selector_screen.dart @@ -225,7 +225,7 @@ class _SymptomsSelectorPageState extends State { children: [ Expanded( child: Text( - '${LocaleKeys.possibleSymptomsRelatedTo.tr(context: context)} "$organName"', + LocaleKeys.possibleSymptomsRelatedTo.tr(context: context), style: TextStyle(fontSize: 18.f, fontWeight: FontWeight.w600, color: AppColors.textColor), ), ), diff --git a/lib/presentation/symptoms_checker/triage_screen.dart b/lib/presentation/symptoms_checker/triage_screen.dart index 6cd4c51e..95de9464 100644 --- a/lib/presentation/symptoms_checker/triage_screen.dart +++ b/lib/presentation/symptoms_checker/triage_screen.dart @@ -82,7 +82,7 @@ class _TriagePageState extends State { // Case 3: Probability >= 70% OR // Case 4: 30 or more questions answered // if (viewModel.shouldStopTriage || highestProbability >= 70.0 || viewModel.triageQuestionCount >= 30) { - if (highestProbability >= 70.0 || viewModel.triageQuestionCount >= 30 || viewModel.shouldStopTriage) { + if (highestProbability >= 70.0 || viewModel.triageQuestionCount >= 4 || viewModel.shouldStopTriage) { // Navigate to results/possible conditions screen context.navigateWithName(AppRoutes.possibleConditionsPage); return; @@ -346,7 +346,7 @@ class _TriagePageState extends State { final hasInstructions = instructions.isNotEmpty; showCommonBottomSheetWithoutHeight( - title: LocaleKeys.howTo.tr(context: context), + title: LocaleKeys.information.tr(context: context), context, child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/presentation/symptoms_checker/user_consent_screen.dart b/lib/presentation/symptoms_checker/user_consent_screen.dart index 90651dc1..8d153de7 100644 --- a/lib/presentation/symptoms_checker/user_consent_screen.dart +++ b/lib/presentation/symptoms_checker/user_consent_screen.dart @@ -8,10 +8,13 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/route_extensions.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/symptoms_checker/symptoms_checker_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.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:provider/provider.dart'; // Before using the checkup, please read the Terms of Service and remember: // @@ -43,6 +46,35 @@ class _UserConsentScreenState extends State { bool get _canProceed => _isTermsAccepted && _isPrivacyAccepted; + void _onNextPressed() async { + final viewModel = context.read(); + final appState = getIt.get(); + + LoaderBottomSheet.showLoader( + loadingText: LocaleKeys.pleaseWait.tr(context: context), + ); + + // For non-authenticated users, fileNo is null + String? fileNo; + if (appState.isAuthenticated) { + final user = appState.getAuthenticatedUser(); + fileNo = user?.patientId.toString(); + } + + await viewModel.getSymptomsUserDetails( + userName: ApiConsts.symptomsCheckerUsername, + password: ApiConsts.symptomsCheckerPassword, + fileNo: fileNo, + onSuccess: () { + LoaderBottomSheet.hideLoader(); + context.navigateWithName(AppRoutes.symptomsSelectorPage); + }, + onError: (String error) { + LoaderBottomSheet.hideLoader(); + }, + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -213,7 +245,7 @@ class _UserConsentScreenState extends State { Expanded( child: CustomButton( text: LocaleKeys.next.tr(context: context), - onPressed: _canProceed ? () => context.navigateWithName(AppRoutes.userInfoSelection) : null, + onPressed: _canProceed ? _onNextPressed : null, isDisabled: !_canProceed, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor, diff --git a/pubspec.yaml b/pubspec.yaml index 40ee9510..71f8d383 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -71,8 +71,8 @@ dependencies: firebase_analytics: ^11.5.1 firebase_crashlytics: ^4.3.8 jiffy: ^6.4.3 -# hijri_gregorian_calendar: -# path: /Users/aamir/StudioProjects/hijri_gregorian_calendar + # hijri_gregorian_calendar: + # path: /Users/aamir/StudioProjects/hijri_gregorian_calendar sms_otp_auto_verify: ^2.2.0 web: any flutter_staggered_animations: ^1.1.1 @@ -89,7 +89,7 @@ dependencies: path: package/flutter_swiper_view flutter_callkit_incoming: ^3.0.0 in_app_update: ^4.2.5 -# device_preview: ^1.3.1 + # device_preview: ^1.3.1 image_picker_android: ^0.8.13+16 image_picker_platform_interface: ^2.10.0