symptoms checker suggestions from infermedia inprogress

pull/327/head
faizatflutter 1 week ago
parent b77b4db2c8
commit 826d12e57d

@ -370,6 +370,12 @@
"clickHere": "انقر هنا",
"iAgreeToTheTermsAndConditions": "أوافق على الشروط والأحكام ",
"iAgreeToTheTermsAndConditionsSubtitle": "أوافق على الشروط والأحكام ",
"iAgreeAndAcceptTermsAndConditions": "أوافق وأقبل ",
"termsAndConditions": "الشروط والأحكام",
"iAgreeHealthInfoCanBeUsed": "أوافق على استخدام معلوماتي الصحية للمقابلة. مزيد من المعلومات في ",
"privacyPolicyText": "سياسة الخصوصية",
"outPatient": "العيادات الخارجيه",
"thisInvoiceIsNotEligibleForRefund": "هذه الفاتورة غير مؤهلة لاسترداد الأموال",
"save": "حفظ",
"userAgreement": "اتفاقية المستخدم",
"updateSuccessfully": "تم التحديث بنجاح",
@ -1893,5 +1899,6 @@
"referenceNo": "رقم المرجع",
"advanceNumber": "رقم الدفعة المقدمة",
"amountOnly": "المبلغ",
"invoiceHistory": "سجل الفواتير"
"invoiceHistory": "سجل الفواتير",
"refund": "استرداد"
}

@ -366,6 +366,12 @@
"clickHere": "Click here",
"iAgreeToTheTermsAndConditions": "I agree to the terms and conditions ",
"iAgreeToTheTermsAndConditionsSubtitle": "I agree to the terms and conditions ",
"iAgreeAndAcceptTermsAndConditions": "I agree and accept ",
"termsAndConditions": "Terms and Conditions",
"iAgreeHealthInfoCanBeUsed": "I agree that my health information can be used for the interview. More information in the ",
"privacyPolicyText": "Privacy Policy",
"outPatient": "Out Patient",
"thisInvoiceIsNotEligibleForRefund": "This invoice is not eligible for refund",
"save": "Save",
"userAgreement": "User Agreement",
"updateSuccessfully": "Update Successfully",
@ -1887,7 +1893,6 @@
"advanceNumber": "Advance Number",
"amountOnly": "Amount",
"invoiceHistory": "Invoice History"
}

@ -255,7 +255,7 @@ class ApiClientImp implements ApiClient {
body: requestBody,
headers: headers,
);
log("response: ${response.body.toString()}");
// log("response: ${response.body.toString()}");
} on SocketException catch (e) {
final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr();
onFailure(message, -1, failureType: ConnectivityFailure(message));

@ -4,7 +4,7 @@ import 'package:hmg_patient_app_new/core/enums.dart';
class ApiConsts {
static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT
@ -1011,33 +1011,9 @@ class ApiKeyConstants {
static final String googleMapsApiKey = 'AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng';
}
//flutter: URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2O_InsertUserActivity
// flutter: {"IdentificationNo":"2530976584","MobileNumber":"504278212","QuantityIntake":200,"VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
// flutter: response.body:
// flutter: {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":true,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":null,"UserProgressForTodayData":[{"QuantityConsumed":200.00,"PercentageConsumed":9.41,"PercentageLeft":90.59,"QuantityLimit":2125.00}],"UserProgressForWeekData":null,"UserProgressHistoryData":[{"Quantity":200.000,"CreatedDate":"\/Date(1766911222217+0300)\/"}],"VerificationCode":null,"isSMSSent":false}
// URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2o_UndoUserActivity
// flutter: {"Progress":1,"MobileNumber":"504278212","IdentificationNo":"2530976584","VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
// flutter: response.body:
// flutter: {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":false,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":null,"UserProgressForTodayData":[{"QuantityConsumed":0.00,"PercentageConsumed":0.00,"PercentageLeft":100.00,"QuantityLimit":2125.00}],"UserProgressForWeekData":null,"UserProgressHistoryData":[],"VerificationCode":null,"isSMSSent":false}
// Progress":2 means weekly data
// flutter: URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2O_GetUserProgress
// flutter: {"Progress":2,"MobileNumber":"504278212","IdentificationNo":"2530976584","VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
// flutter: response.body:
// [log] {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":false,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":null,"UserProgressForTodayData":null,"UserProgressForWeekData":[{"DayNumber":1,"DayDate":null,"DayName":"Sunday","PercentageConsumed":0},{"DayNumber":7,"DayDate":null,"DayName":"Saturday","PercentageConsumed":0},{"DayNumber":6,"DayDate":null,"DayName":"Friday","PercentageConsumed":0},{"DayNumber":5,"DayDate":null,"DayName":"Thursday","PercentageConsumed":0},{"DayNumber":4,"DayDate":null,"DayName":"Wednesday","PercentageConsumed":0},{"DayNumber":3,"DayDate":null,"DayName":"Tuesday","PercentageConsumed":0},{"DayNumber":2,"DayDate":null,"DayName":"Monday","PercentageConsumed":0}],"UserProgressHistoryData":[],"VerificationCode":null,"isSMSSent":false}
// Progress":1 means daily data
//URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2O_GetUserProgress
// flutter: {"Progress":1,"MobileNumber":"504278212","IdentificationNo":"2530976584","VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
// flutter: response.body:
// flutter: {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":false,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":null,"UserProgressForTodayData":[{"QuantityConsumed":0.00,"PercentageConsumed":0.00,"PercentageLeft":100.00,"QuantityLimit":2125.00}],"UserProgressForWeekData":null,"UserProgressHistoryData":[],"VerificationCode":null,"isSMSSent":false}
// Progress":1 means monthly data
// flutter: URL : https://hmgwebservices.com/Services/H2ORemainder.svc/REST/H2O_GetUserProgress
// flutter: {"Progress":3,"MobileNumber":"504278212","IdentificationNo":"2530976584","VersionID":20.0,"Channel":3,"LanguageID":2,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","PatientOutSA":0,"SessionID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","isDentalAllowedBackend":false,"DeviceTypeID":1,"PatientID":4515697,"TokenID":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblR5cGUiOjIsIm9iaiI6eyJUaW1lIjoiMjAyNS0xMi0yOFQwODozNjo0My45MTY0MzRaIiwiUGF0aWVudElEIjoiNDUxNTY5NyIsIlBhdGllbnRNZXJnZWRJRHMiOiI0NTE1Njk3IiwiUGF0aWVudE91dFNBIjpmYWxzZX0sImV4cCI6MTc2Njk2NjQwMH0.l1rCcs2OAU5u2J-iZMiO7NX6shGzLJV0hlYtVh2JeqY","PatientTypeID":1,"PatientType":1,"Latitude":37.785834,"Longitude":-122.406417}
// flutter: response.body:
// [log] {"Date":null,"LanguageID":0,"ServiceName":0,"Time":null,"AndroidLink":null,"AuthenticationTokenID":null,"Data":null,"Dataw":false,"DietType":0,"DietTypeID":0,"ErrorCode":null,"ErrorEndUserMessage":null,"ErrorEndUserMessageN":null,"ErrorMessage":null,"ErrorStatusCode":0,"ErrorType":0,"FoodCategory":0,"IOSLink":null,"IsAuthenticated":false,"MealOrderStatus":0,"MealType":0,"MessageStatus":1,"NumberOfResultRecords":0,"PatientBlodType":null,"SuccessMsg":null,"SuccessMsgN":null,"VidaUpdatedResponse":null,"IsHMGPatient":false,"LogInTokenID":null,"PhysicalActivityData":null,"RowExists":0,"UserDetailData":null,"UserDetailData_New":null,"UserProgressForMonthData":[{"MonthNumber":1,"MonthName":"January","PercentageConsumed":0},{"MonthNumber":2,"MonthName":"February","PercentageConsumed":0},{"MonthNumber":3,"MonthName":"March","PercentageConsumed":0},{"MonthNumber":4,"MonthName":"April","PercentageConsumed":0},{"MonthNumber":5,"MonthName":"May","PercentageConsumed":0},{"MonthNumber":6,"MonthName":"June","PercentageConsumed":0},{"MonthNumber":7,"MonthName":"July","PercentageConsumed":0},{"MonthNumber":8,"MonthName":"August","PercentageConsumed":0},{"MonthNumber":9,"MonthName":"September","PercentageConsumed":0},{"MonthNumber":10,"MonthName":"October","PercentageConsumed":0},{"MonthNumber":11,"MonthName":"November","PercentageConsumed":0},{"MonthNumber":12,"MonthName":"December","PercentageConsumed":0}],"UserProgressForTodayData":null,"UserProgressForWeekData":null,"UserProgressHistoryData":[],"VerificationCode":null,"isSMSSent":false}
class AppUrls {
static final hmgTermsUrlEn = 'https://hmg.com/en/Pages/Terms.aspx';
static final hmgPrivacyUrlEn = 'https://hmg.com/en/Pages/Privacy.aspx';
static final hmgTermsUrlAr = 'https://hmg.com/ar/Pages/Terms.aspx';
static final hmgPrivacyUrlAr = 'https://hmg.com/ar/Pages/Privacy.aspx';
}

@ -40,7 +40,7 @@ abstract class SymptomsCheckerRepo {
Future<Either<Failure, GenericApiModel<RiskAndSuggestionsResponseModel>>> getSuggestions({
required int age,
required int gender,
required List<String> evidenceIds,
required List<Map<String, dynamic>> evidenceList,
required String language,
required String userSessionToken,
required String sessionId,
@ -49,7 +49,7 @@ abstract class SymptomsCheckerRepo {
Future<Either<Failure, GenericApiModel<TriageDataDetails>>> getDiagnosisForTriage({
required int age,
required int gender,
required List<String> evidenceIds,
required List<Map<String, dynamic>> initialEvidenceList,
List<Map<String, String>>? triageEvidence,
required String language,
required String userSessionToken,
@ -201,12 +201,20 @@ class SymptomsCheckerRepoImp implements SymptomsCheckerRepo {
required String userSessionToken,
required String sessionId,
}) async {
// Build evidence list with "initial" source for symptoms
final List<Map<String, dynamic>> evidenceList = evidenceIds.map((id) {
return {
"id": id,
"source": "initial",
};
}).toList();
final Map<String, dynamic> body = {
"age": {
"value": age,
},
"gender": gender,
"evidence": evidenceIds.map((id) => {"id": id}).toList(),
"evidence": evidenceList,
"language": language,
"generalId": sessionId,
};
@ -265,21 +273,19 @@ class SymptomsCheckerRepoImp implements SymptomsCheckerRepo {
Future<Either<Failure, GenericApiModel<TriageDataDetails>>> getDiagnosisForTriage({
required int age,
required int gender,
required List<String> evidenceIds,
required List<Map<String, dynamic>> initialEvidenceList,
List<Map<String, String>>? triageEvidence, // Additional triage-specific evidence
required String language,
required String userSessionToken,
required String sessionId,
}) async {
// Build evidence list: combine initial symptoms with triage evidence
// Build evidence list: combine initial evidence (with sources) with triage evidence
List<Map<String, dynamic>> evidenceList = [];
// Add initial evidence as simple IDs
for (var id in evidenceIds) {
evidenceList.add({"id": id});
}
// Add initial evidence (already has sources: initial, red_flags, suggest)
evidenceList.addAll(initialEvidenceList);
// Add triage evidence with proper format (id, choice_id, source)
// Add triage evidence with proper format (id, choice_id, source=triage)
if (triageEvidence != null && triageEvidence.isNotEmpty) {
evidenceList.addAll(triageEvidence);
}
@ -351,7 +357,7 @@ class SymptomsCheckerRepoImp implements SymptomsCheckerRepo {
Future<Either<Failure, GenericApiModel<RiskAndSuggestionsResponseModel>>> getSuggestions({
required int age,
required int gender,
required List<String> evidenceIds,
required List<Map<String, dynamic>> evidenceList,
required String language,
required String userSessionToken,
required String sessionId,
@ -361,7 +367,7 @@ class SymptomsCheckerRepoImp implements SymptomsCheckerRepo {
"value": age,
},
"gender": gender,
"evidence": evidenceIds.map((id) => {"id": id}).toList(),
"evidence": evidenceList,
"language": language,
"generalId": sessionId,
};

@ -87,6 +87,10 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
String? _selectedSingleItemId; // Store which item was selected
int? _selectedSingleChoiceIndex; // Store which choice was selected
// Triage question history tracking
final List<TriageDataDetails> _triageQuestionHistory = []; // Stack of previous questions
final List<List<Map<String, String>>> _triageEvidenceHistory = []; // Stack of evidence states at each question
// Selected risk factors tracking
final Set<String> _selectedRiskFactorIds = {};
@ -178,6 +182,9 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
/// Get the number of triage questions answered
int get triageQuestionCount => _triageQuestionCount;
/// Check if this is the first triage question (no history)
bool get isFirstTriageQuestion => _triageQuestionHistory.isEmpty;
List<GetClinicDetailsResponseModel> clinicDetailsList = [];
/// Get choice index for a specific item
@ -745,6 +752,44 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
return evidenceIds;
}
/// Get all evidence with their sources based on selection step
/// Source values:
/// - "initial" for symptoms (used in GetRiskFactors)
/// - "red_flags" for risk factors (used in GetSuggestions)
/// - "suggest" for suggestions (used in GetDiagnosis)
List<Map<String, dynamic>> getAllEvidenceWithSources({String defaultSource = "initial"}) {
List<Map<String, dynamic>> evidenceList = [];
// Add selected symptoms with source "initial"
final selectedSymptoms = getAllSelectedSymptoms();
for (var symptom in selectedSymptoms.where((s) => s.id != null)) {
evidenceList.add({
"id": symptom.id!,
"source": "initial",
});
}
// Add selected risk factors with source "red_flags" (excluding "not_applicable")
final selectedRiskFactors = getAllSelectedRiskFactors();
for (var riskFactor in selectedRiskFactors.where((rf) => rf.id != null && rf.id != "not_applicable")) {
evidenceList.add({
"id": riskFactor.id!,
"source": "red_flags",
});
}
// Add selected suggestions with source "suggest" (excluding "not_applicable")
final selectedSuggestions = getAllSelectedSuggestions();
for (var suggestion in selectedSuggestions.where((s) => s.id != null && s.id != "not_applicable")) {
evidenceList.add({
"id": suggestion.id!,
"source": "suggest",
});
}
return evidenceList;
}
/// Fetch risk factors based on selected symptoms
Future<void> fetchSuggestions({
Function()? onSuccess,
@ -768,21 +813,30 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
return;
}
// Extract symptom IDs
List<String> evidenceIds = selectedSymptoms.where((s) => s.id != null).map((s) => s.id!).toList();
// Build evidence list with proper sources
List<Map<String, dynamic>> evidenceList = [];
// Get all selected symptoms
final selectedRisks = getAllSelectedRiskFactors();
// Add selected symptoms with source "initial"
for (var symptom in selectedSymptoms.where((s) => s.id != null)) {
evidenceList.add({
"id": symptom.id!,
"source": "initial",
});
}
if (selectedRisks.isNotEmpty) {
List<String> evidenceRisksIds = selectedRisks.where((s) => s.id != null && s.id != "not_applicable").map((s) => s.id!).toList();
evidenceIds.addAll(evidenceRisksIds);
// Get selected risk factors and add with source "red_flags"
final selectedRisks = getAllSelectedRiskFactors();
for (var riskFactor in selectedRisks.where((rf) => rf.id != null && rf.id != "not_applicable")) {
evidenceList.add({
"id": riskFactor.id!,
"source": "red_flags",
});
}
await getSuggestions(
age: _selectedAge!,
gender: _getGenderAsInt(_selectedGender),
evidenceIds: evidenceIds,
evidenceList: evidenceList,
language: appState.isArabic() ? 'ar' : 'en',
onSuccess: (response) {
if (onSuccess != null) {
@ -801,7 +855,7 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
Future<void> getSuggestions({
required int age,
required int gender,
required List<String> evidenceIds,
required List<Map<String, dynamic>> evidenceList,
required String language,
Function(RiskAndSuggestionsResponseModel)? onSuccess,
Function(String)? onError,
@ -812,7 +866,7 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
final result = await symptomsCheckerRepo.getSuggestions(
age: age,
gender: gender,
evidenceIds: evidenceIds,
evidenceList: evidenceList,
language: language,
sessionId: currentSessionId,
userSessionToken: currentSessionAuthToken,
@ -863,19 +917,27 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
Future<void> getDiagnosisForTriage({
required int age,
required int gender,
required List<String> evidenceIds,
List<Map<String, String>>? triageEvidence,
required List<Map<String, dynamic>> initialEvidenceList, // Evidence with sources (initial, red_flags, suggest)
List<Map<String, String>>? triageEvidence, // Triage-specific evidence (id, choice_id, source=triage)
required String language,
Function(TriageDataDetails)? onSuccess,
Function(String)? onError,
bool isGoingBack = false, // Flag to indicate if we're going back in history
}) async {
// Save current question and evidence to history BEFORE making new API call
// (but only if we're moving forward, not going back)
if (!isGoingBack && triageDataDetails != null) {
_triageQuestionHistory.add(triageDataDetails!);
_triageEvidenceHistory.add(List<Map<String, String>>.from(_triageEvidenceList));
}
isTriageDiagnosisLoading = true;
notifyListeners();
final result = await symptomsCheckerRepo.getDiagnosisForTriage(
age: age,
gender: gender,
evidenceIds: evidenceIds,
initialEvidenceList: initialEvidenceList,
triageEvidence: triageEvidence,
language: language,
sessionId: currentSessionId,
@ -923,10 +985,10 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
return;
}
// Get all evidence IDs
final evidenceIds = getAllEvidenceIds();
// Get all evidence with sources
final evidenceList = getAllEvidenceWithSources();
if (evidenceIds.isEmpty) {
if (evidenceList.isEmpty) {
if (onError != null) {
onError('No evidence selected');
}
@ -936,7 +998,7 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
await getDiagnosisForTriage(
age: _selectedAge!,
gender: _getGenderAsInt(_selectedGender),
evidenceIds: evidenceIds,
initialEvidenceList: evidenceList,
language: appState.isArabic() ? 'ar' : 'en',
onSuccess: (response) {
if (onSuccess != null) {
@ -993,7 +1055,7 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
_triageEvidenceList.add({
"id": itemId,
"choice_id": choiceId,
"source": "triage",
"source": "suggest",
});
notifyListeners();
}
@ -1009,6 +1071,39 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
notifyListeners();
}
/// Go back to previous triage question
/// Returns true if successfully went back, false if already at first question
bool goBackToPreviousTriageQuestion() {
if (_triageQuestionHistory.isEmpty) {
return false; // Already at first question
}
// Pop the last question from history
final previousQuestion = _triageQuestionHistory.removeLast();
final previousEvidence = _triageEvidenceHistory.removeLast();
// Restore the previous question
triageDataDetails = previousQuestion;
// Restore the previous evidence state
_triageEvidenceList.clear();
_triageEvidenceList.addAll(previousEvidence);
// Decrement question count
if (_triageQuestionCount > 0) {
_triageQuestionCount--;
}
// Clear current selections
_selectedTriageChoiceIndex = null;
_selectedTriageChoicesByItemId.clear();
_selectedSingleItemId = null;
_selectedSingleChoiceIndex = null;
notifyListeners();
return true;
}
void reset() {
_currentView = BodyView.front;
_selectedOrganIds.clear();
@ -1018,6 +1113,8 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
_triageEvidenceList.clear();
_selectedTriageChoicesByItemId.clear();
_triageQuestionCount = 0; // Reset question count
_triageQuestionHistory.clear(); // Clear question history
_triageEvidenceHistory.clear(); // Clear evidence history
_currentZoomScale = 1.0; // Reset zoom scale
_symptomSearchQuery = ''; // Reset search query
_filteredOrganSymptomsResults.clear(); // Clear filtered results
@ -1054,6 +1151,8 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
_selectedRiskFactorIds.clear();
_selectedSuggestionsIds.clear();
_triageEvidenceList.clear();
_triageQuestionHistory.clear(); // Clear question history
_triageEvidenceHistory.clear(); // Clear evidence history
_selectedTriageChoicesByItemId.clear();
_selectedSingleItemId = null;
_selectedSingleChoiceIndex = null;
@ -1066,6 +1165,28 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
notifyListeners();
}
/// Clear selections when reaching possible conditions screen
/// This clears organs, symptoms, risk factors, suggestions, and questions
void clearSelectionsForPossibleConditions() {
_selectedOrganIds.clear();
_selectedSymptomsByOrgan.clear();
_selectedRiskFactorIds.clear();
_selectedSuggestionsIds.clear();
_triageEvidenceList.clear();
_triageQuestionHistory.clear();
_triageEvidenceHistory.clear();
_selectedTriageChoicesByItemId.clear();
_selectedSingleItemId = null;
_selectedSingleChoiceIndex = null;
_triageQuestionCount = 0;
_symptomSearchQuery = '';
_filteredOrganSymptomsResults.clear();
_isBottomSheetExpanded = false;
_tooltipTimer?.cancel();
_tooltipOrganId = null;
notifyListeners();
}
// User Info Flow Methods
/// Set current page in user info flow
@ -1170,6 +1291,34 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
};
}
// User Consent Methods
bool isTermsAccepted = false;
bool isPrivacyAccepted = false;
/// Toggle terms acceptance
void toggleTermsAcceptance() {
isTermsAccepted = !isTermsAccepted;
notifyListeners();
}
/// Toggle privacy policy acceptance
void togglePrivacyAcceptance() {
isPrivacyAccepted = !isPrivacyAccepted;
notifyListeners();
}
void setTermsAcceptance(bool value) {
isTermsAccepted = value;
notifyListeners();
}
/// Toggle privacy policy acceptance
void setPrivacyAcceptance(bool value) {
isPrivacyAccepted = value;
notifyListeners();
}
Future<void> getSymptomsUserDetails({
required String userName,
required String password,

File diff suppressed because it is too large Load Diff

@ -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';
@ -371,6 +371,12 @@ abstract class LocaleKeys {
static const clickHere = 'clickHere';
static const iAgreeToTheTermsAndConditions = 'iAgreeToTheTermsAndConditions';
static const iAgreeToTheTermsAndConditionsSubtitle = 'iAgreeToTheTermsAndConditionsSubtitle';
static const iAgreeAndAcceptTermsAndConditions = 'iAgreeAndAcceptTermsAndConditions';
static const termsAndConditions = 'termsAndConditions';
static const iAgreeHealthInfoCanBeUsed = 'iAgreeHealthInfoCanBeUsed';
static const privacyPolicyText = 'privacyPolicyText';
static const outPatient = 'outPatient';
static const thisInvoiceIsNotEligibleForRefund = 'thisInvoiceIsNotEligibleForRefund';
static const save = 'save';
static const userAgreement = 'userAgreement';
static const updateSuccessfully = 'updateSuccessfully';
@ -1360,7 +1366,6 @@ abstract class LocaleKeys {
static const medicalFiles = 'medicalFiles';
static const addANewFamilyMember = 'addANewFamilyMember';
static const viewInvoiceDetails = 'viewInvoiceDetails';
static const outPatient = 'outPatient';
static const invoiceDetails = 'invoiceDetails';
static const sendingEmailPleaseWait = 'sendingEmailPleaseWait';
static const emailSentSuccessfullyMessage = 'emailSentSuccessfullyMessage';
@ -1851,7 +1856,7 @@ abstract class LocaleKeys {
static const invalidIbanFormat = 'invalidIbanFormat';
static const refundHistory = 'refundHistory';
static const noRefundHistoryFound = 'noRefundHistoryFound';
static const selectProcedures = 'selectProcedures';
static const selectProcedure = 'selectProcedure';
static const noProceduresAvailableForRefund = 'noProceduresAvailableForRefund';
static const selectAll = 'selectAll';
static const selectRefundMethod = 'selectRefundMethod';
@ -1866,18 +1871,19 @@ abstract class LocaleKeys {
static const refundReason5 = 'refundReason5';
static const refundReason6 = 'refundReason6';
static const refundReason7 = 'refundReason7';
static const selectProcedures = 'selectProcedures';
static const redeem = 'redeem';
static const points = 'points';
static const transactions = 'transactions';
static const pharmacy = 'pharmacy';
static const visitsOrders = 'visitsOrders';
static const cannotOpenThisFile = 'cannotOpenThisFile';
static const thisInvoiceIsNotEligibleForRefund = 'thisInvoiceIsNotEligibleForRefund';
static const refundDetails = 'refundDetails';
static const requestNo = 'requestNo';
static const referenceNo = 'referenceNo';
static const refund = 'refund';
static const advanceNumber = 'advanceNumber';
static const amountOnly = 'amountOnly';
static const invoiceHistory = 'invoiceHistory';
static const refund = 'refund';
}

@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.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';
@ -95,7 +96,13 @@ class _RegisterNew extends State<RegisterNew> {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Utils.showLottie(context: context, assetPath: 'assets/animations/lottie/register.json', width: 200.w, height: 200.h, fit: BoxFit.cover, repeat: true),
Utils.showLottie(
context: context,
assetPath: 'assets/animations/lottie/register.json',
width: 200.w,
height: 200.h,
fit: BoxFit.cover,
repeat: true),
SizedBox(height: 16.h),
LocaleKeys.prepareToElevate.tr(context: context).toText32(isBold: true),
SizedBox(height: 24.h),
@ -134,8 +141,10 @@ class _RegisterNew extends State<RegisterNew> {
padding: EdgeInsets.symmetric(vertical: 8.h),
leadingIcon: AppAssets.student_card,
fontFamily: "Poppins",
hasError: false, // Don't show individual field border
errorMessage: authVm.nationalIdError, // Show error message if exists
hasError: false,
// Don't show individual field border
errorMessage: authVm.nationalIdError,
// Show error message if exists
onChange: (value) {
// Clear error when user starts typing
authVm.clearNationalIdError();
@ -162,8 +171,9 @@ class _RegisterNew extends State<RegisterNew> {
authVm.clearDobError();
},
fontFamily: "Poppins",
hasError: false, // Don't show individual field border
errorMessage: authVm.dobError, // Show error message if exists
hasError: false,
// Don't show individual field border
errorMessage: authVm.dobError, // Show error message if exists
).withVerticalPadding(8),
],
),
@ -202,7 +212,7 @@ class _RegisterNew extends State<RegisterNew> {
onTap: () {
// Navigate to terms and conditions page
Utils.openWebView(
url: 'https://hmg.com/en/Pages/Terms.aspx',
url: AppUrls.hmgTermsUrlEn,
);
},
child: Text(

@ -1,5 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.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';
@ -15,18 +16,13 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/blood_donation/widgets/select_blood_group_widget.dart';
import 'package:hmg_patient_app_new/presentation/blood_donation/widgets/select_city_widget.dart';
import 'package:hmg_patient_app_new/presentation/blood_donation/widgets/select_gender_widget.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/services/dialog_service.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/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:lottie/lottie.dart';
import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart';
class BloodDonationPage extends StatefulWidget {
BloodDonationPage({super.key});
@ -60,7 +56,8 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
onPressed: () {
if (bloodDonationVM.isUserAuthanticated()) {
bloodDonationVM.fetchHospitalsList().then((value) {
showCommonBottomSheetWithoutHeight(context, title: LocaleKeys.selectHospital.tr(context: context), isDismissible: false, child: Consumer<BloodDonationViewModel>(builder: (_, data, __) {
showCommonBottomSheetWithoutHeight(context, title: LocaleKeys.selectHospital.tr(context: context), isDismissible: false,
child: Consumer<BloodDonationViewModel>(builder: (_, data, __) {
return HospitalBottomSheetBodySelection(
isHideTitle: true,
onUserHospitalSelection: (BdGetProjectsHaveBdClinic userChoice) {
@ -77,7 +74,8 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Lottie.asset(AppAnimations.errorAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
Lottie.asset(AppAnimations.errorAnimation,
repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
(LocaleKeys.loginToUseService.tr()).toText16(color: AppColors.blackColor),
SizedBox(height: 16.h),
@ -131,7 +129,8 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
padding: EdgeInsets.all(24.w),
child: SingleChildScrollView(
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false),
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
@ -150,7 +149,8 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
LocaleKeys.city.tr().toText16(color: AppColors.textColor, isBold: true),
(appState.isArabic()
? (bloodDonationVM.selectedCity?.descriptionN ?? LocaleKeys.selectBloodDonationCity.tr())
: bloodDonationVM.selectedCity?.description ?? LocaleKeys.selectBloodDonationCity.tr(context: context))
: bloodDonationVM.selectedCity?.description ??
LocaleKeys.selectBloodDonationCity.tr(context: context))
.toText14(color: AppColors.greyTextColor, isBold: true),
],
),
@ -160,7 +160,10 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
],
).onPress(() async {
showCommonBottomSheetWithoutHeight(context,
title: LocaleKeys.selectCity.tr(context: context), isDismissible: true, child: SelectCityWidget(bloodDonationViewModel: bloodDonationVM), callBackFunc: () {});
title: LocaleKeys.selectCity.tr(context: context),
isDismissible: true,
child: SelectCityWidget(bloodDonationViewModel: bloodDonationVM),
callBackFunc: () {});
}),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
@ -254,17 +257,17 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
SizedBox(width: 12.w),
Expanded(
child: LocaleKeys.aboutBloodDonation.tr(context: context).toText16(
color: AppColors.primaryRedColor,
isBold: true,
),
color: AppColors.primaryRedColor,
isBold: true,
),
),
],
),
SizedBox(height: 12.h),
LocaleKeys.bloodDonationDescription.tr(context: context).toText14(
color: AppColors.greyTextColor,
isBold: true,
),
color: AppColors.greyTextColor,
isBold: true,
),
SizedBox(height: 12.h),
Row(
children: [
@ -279,9 +282,9 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
SizedBox(width: 8.w),
Expanded(
child: LocaleKeys.bloodDonationBenefit1.tr(context: context).toText13(
color: AppColors.textColor,
isBold: true,
),
color: AppColors.textColor,
isBold: true,
),
),
],
),
@ -299,9 +302,9 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
SizedBox(width: 8.w),
Expanded(
child: LocaleKeys.bloodDonationBenefit2.tr(context: context).toText13(
color: AppColors.textColor,
isBold: true,
),
color: AppColors.textColor,
isBold: true,
),
),
],
),
@ -319,9 +322,9 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
SizedBox(width: 8.w),
Expanded(
child: LocaleKeys.bloodDonationBenefit3.tr(context: context).toText13(
color: AppColors.textColor,
isBold: true,
),
color: AppColors.textColor,
isBold: true,
),
),
],
),
@ -376,10 +379,10 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
// Navigator.of(context).pushNamed('/terms');
appState.isArabic()
? Utils.openWebView(
url: 'https://hmg.com/ar/Pages/Terms.aspx',
url: AppUrls.hmgTermsUrlAr,
)
: Utils.openWebView(
url: 'https://hmg.com/en/Pages/Terms.aspx',
url: AppUrls.hmgTermsUrlEn,
);
},
child: Text(
@ -419,7 +422,8 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Lottie.asset(AppAnimations.errorAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
Lottie.asset(AppAnimations.errorAnimation,
repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
(LocaleKeys.loginToUseService.tr()).toText16(color: AppColors.blackColor),
SizedBox(height: 16.h),

@ -1,6 +1,7 @@
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/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
@ -820,7 +821,7 @@ class _ServicesPageState extends State<ServicesPage> {
),
).onPress(() {
Utils.openWebView(
url: 'https://hmg.com/en/Pages/Terms.aspx',
url: AppUrls.hmgTermsUrlEn,
);
}),
),
@ -845,7 +846,7 @@ class _ServicesPageState extends State<ServicesPage> {
),
).onPress(() {
Utils.openWebView(
url: 'https://hmg.com/en/Pages/Privacy.aspx',
url: AppUrls.hmgPrivacyUrlEn,
);
}),
)

@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/api_consts.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/cache_consts.dart';
@ -82,7 +83,8 @@ class MonthlyReport extends StatelessWidget {
LocaleKeys.email.tr(context: context).toText12(color: AppColors.greyTextColor, isBold: true),
SizedBox(
width: MediaQuery.of(context).size.width * 0.7,
child: "${appState.getAuthenticatedUser()!.emailAddress}".toText16(color: AppColors.textColor, isBold: true, maxlines: 2),
child: "${appState.getAuthenticatedUser()!.emailAddress}"
.toText16(color: AppColors.textColor, isBold: true, maxlines: 2),
),
],
),
@ -98,7 +100,9 @@ class MonthlyReport extends StatelessWidget {
Utils.buildSvgWithAssets(icon: AppAssets.prescription_remarks_icon, width: 18.w, height: 18.h),
SizedBox(width: 9.h),
Expanded(
child: LocaleKeys.monthlyHealthSummaryReportDisclaimer.tr(context: context).toText10(isBold: true, color: AppColors.greyTextColorLight),
child: LocaleKeys.monthlyHealthSummaryReportDisclaimer
.tr(context: context)
.toText10(isBold: true, color: AppColors.greyTextColorLight),
),
],
),
@ -135,9 +139,11 @@ class MonthlyReport extends StatelessWidget {
),
SizedBox(width: 10.w),
"I agree to the ".toText14(isBold: true, letterSpacing: -1.0),
"terms and conditions".toText14(isBold: true, letterSpacing: -1.0, color: AppColors.primaryRedColor, isUnderLine: true).onPress(() {
"terms and conditions"
.toText14(isBold: true, letterSpacing: -1.0, color: AppColors.primaryRedColor, isUnderLine: true)
.onPress(() {
Utils.openWebView(
url: 'https://hmg.com/en/Pages/Terms.aspx',
url: AppUrls.hmgTermsUrlEn,
);
})
],

@ -24,9 +24,9 @@ import 'package:provider/provider.dart';
class InvoiceListCard extends StatelessWidget {
final GetInvoicesListResponseModel getInvoicesListResponseModel;
Function? onTap;
final Function? onTap;
InvoiceListCard({super.key, required this.getInvoicesListResponseModel, required this.onTap});
const InvoiceListCard({super.key, required this.getInvoicesListResponseModel, required this.onTap});
void _handleRefundInvoiceTap(BuildContext context) {
final invoiceNo = getInvoicesListResponseModel.invoiceNo;
@ -70,7 +70,6 @@ class InvoiceListCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
AppState appState = getIt<AppState>();
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
child: Padding(
@ -91,7 +90,7 @@ class InvoiceListCard extends StatelessWidget {
textColor: AppColors.textColor,
),
AppCustomChipWidget(
labelText: appState.isArabic() ? "العيادات الخارجيه" : "Out Patient",
labelText: LocaleKeys.outPatient.tr(context: context),
backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.1),
textColor: AppColors.warningColorYellow,
),

@ -4,6 +4,7 @@ import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_swiper_view/flutter_swiper_view.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
@ -347,13 +348,13 @@ class ProfileSettingsState extends State<ProfileSettings> {
1.divider,
actionItem(AppAssets.privacy_terms, LocaleKeys.privacyPolicy.tr(context: context), () {
Utils.openWebView(
url: 'https://hmg.com/en/Pages/Privacy.aspx',
url: AppUrls.hmgPrivacyUrlEn,
);
}, isExternalLink: true),
1.divider,
actionItem(AppAssets.privacy_terms, LocaleKeys.termsConditoins.tr(context: context), () {
Utils.openWebView(
url: 'https://hmg.com/en/Pages/Terms.aspx',
url: AppUrls.hmgTermsUrlEn,
);
}, isExternalLink: true),
1.divider,

@ -49,8 +49,8 @@ class _PossibleConditionsPageState extends State<PossibleConditionsPage> {
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
// Clear selections once user reaches results page
symptomsCheckerViewModel.clearSelectionsKeepResults();
// Clear all selections (organs, symptoms, risk factors, suggestions, questions) once user reaches possible conditions screen
symptomsCheckerViewModel.clearSelectionsForPossibleConditions();
});
}

@ -157,7 +157,13 @@ class _TriagePageState extends State<TriagePage> {
}
void _onPreviousPressed() {
context.pop();
// Try to go back to previous question
bool wentBack = viewModel.goBackToPreviousTriageQuestion();
if (!wentBack) {
// If already at first question, go back to suggestions screen
context.pop();
}
}
void _onNextPressed() {
@ -224,15 +230,15 @@ class _TriagePageState extends State<TriagePage> {
}
}
// Get all evidence: initial symptoms + risk factors + suggestions + triage evidence
List<String> initialEvidenceIds = viewModel.getAllEvidenceIds();
// Get all evidence: initial symptoms + risk factors + suggestions (with sources) + triage evidence
List<Map<String, dynamic>> initialEvidenceList = viewModel.getAllEvidenceWithSources();
List<Map<String, String>> triageEvidence = viewModel.getTriageEvidence();
// Call API with updated evidence
viewModel.getDiagnosisForTriage(
age: viewModel.selectedAge!,
gender: viewModel.selectedGender!.toLowerCase() == "male" ? 1 : 2,
evidenceIds: initialEvidenceIds,
initialEvidenceList: initialEvidenceList,
triageEvidence: triageEvidence,
language: viewModel.appState.isArabic() ? 'ar' : 'en',
onSuccess: (response) {
@ -536,7 +542,7 @@ class _TriagePageState extends State<TriagePage> {
children: [
RichText(
text: TextSpan(
text: "${LocaleKeys.possibleSymptom.tr(context: context)} ",
text: "${LocaleKeys.possibleConditions.tr(context: context)} ",
style: TextStyle(
color: AppColors.greyTextColor, fontWeight: FontWeight.w600, fontSize: 14.f, fontFamily: isArabic ? 'CairoArabic' : 'Poppins'),
children: [
@ -586,8 +592,8 @@ class _TriagePageState extends State<TriagePage> {
Expanded(
child: CustomButton(
text: LocaleKeys.previous.tr(context: context),
onPressed: isFirstQuestion ? () {} : _onPreviousPressed,
isDisabled: isFirstQuestion || viewModel.isTriageDiagnosisLoading,
onPressed: _onPreviousPressed,
isDisabled: viewModel.isTriageDiagnosisLoading,
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.11),
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,

@ -0,0 +1,231 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_export.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/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/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';
// Before using the checkup, please read the Terms of Service and remember:
//
// Checkup isn't a diagnosis. It's only for your information and not a qualified medical opinion.
// Checkup isn't for emergencies. Call your local emergency number right away when there's a health emergency.
// Your data is safe. Privacy measures ensure the information you share is protected.
class UserConsentScreen extends StatefulWidget {
const UserConsentScreen({super.key});
@override
State<UserConsentScreen> createState() => _UserConsentScreenState();
}
class _UserConsentScreenState extends State<UserConsentScreen> {
bool _isTermsAccepted = false;
bool _isPrivacyAccepted = false;
void _onTermsAndConditionsAgreed() {
setState(() {
_isTermsAccepted = !_isTermsAccepted;
});
}
void _onPrivacyPolicyAgreed() {
setState(() {
_isPrivacyAccepted = !_isPrivacyAccepted;
});
}
bool get _canProceed => _isTermsAccepted && _isPrivacyAccepted;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(
children: [
Expanded(
child: CollapsingListView(
title: LocaleKeys.termsService.tr(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Before using the checkup, please read the Terms of Service and remember:".toText14(),
SizedBox(height: 44.h),
"• Checkup isn't a diagnosis. It's only for your information and not a qualified medical opinion.".toText14(),
SizedBox(height: 12.h),
"• Checkup isn't for emergencies. Call your local emergency number right away when there's a health emergency.".toText14(),
SizedBox(height: 12.h),
"• Your data is safe. Privacy measures ensure the information you share is protected.".toText14(),
SizedBox(height: 24.h),
GestureDetector(
onTap: _onTermsAndConditionsAgreed,
child: Container(
margin: EdgeInsets.only(bottom: 12.h),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: 24.w,
height: 24.w,
decoration: BoxDecoration(
color: _isTermsAccepted ? AppColors.primaryRedColor : Colors.transparent,
borderRadius: BorderRadius.circular(5.r),
border: Border.all(
color: _isTermsAccepted ? AppColors.primaryRedColor : AppColors.checkBoxBorderColor,
width: 1.w,
),
),
child: _isTermsAccepted ? Icon(Icons.check, size: 16.f, color: AppColors.whiteColor) : null,
),
SizedBox(width: 12.w),
Expanded(
child: RichText(
text: TextSpan(
text: LocaleKeys.iAgreeAndAcceptTermsAndConditions.tr(context: context),
style: TextStyle(
fontSize: 16.f,
fontFamily: getIt.get<AppState>().isArabic() ? 'CairoArabic' : 'Poppins',
fontWeight: FontWeight.w500,
color: AppColors.textColor,
),
children: [
WidgetSpan(
child: GestureDetector(
onTap: () {
AppState appState = getIt.get<AppState>();
Utils.openWebView(
url: appState.isArabic() ? AppUrls.hmgTermsUrlAr : AppUrls.hmgTermsUrlEn,
);
},
child: Text(
LocaleKeys.termsAndConditions.tr(context: context),
style: TextStyle(
fontSize: 16.f,
fontFamily: getIt.get<AppState>().isArabic() ? 'CairoArabic' : 'Poppins',
fontWeight: FontWeight.w500,
color: AppColors.primaryRedColor,
decoration: TextDecoration.underline,
decorationColor: AppColors.primaryRedColor,
),
),
),
),
],
),
),
),
],
),
),
),
SizedBox(
height: 16.h,
),
GestureDetector(
onTap: _onPrivacyPolicyAgreed,
child: Container(
margin: EdgeInsets.only(bottom: 12.h),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: 24.w,
height: 24.w,
decoration: BoxDecoration(
color: _isPrivacyAccepted ? AppColors.primaryRedColor : Colors.transparent,
borderRadius: BorderRadius.circular(5.r),
border: Border.all(
color: _isPrivacyAccepted ? AppColors.primaryRedColor : AppColors.checkBoxBorderColor,
width: 1.w,
),
),
child: _isPrivacyAccepted ? Icon(Icons.check, size: 16.f, color: AppColors.whiteColor) : null,
),
SizedBox(width: 12.w),
Expanded(
child: RichText(
text: TextSpan(
text: LocaleKeys.iAgreeHealthInfoCanBeUsed.tr(context: context),
style: TextStyle(
fontSize: 16.f,
fontFamily: getIt.get<AppState>().isArabic() ? 'CairoArabic' : 'Poppins',
fontWeight: FontWeight.w500,
color: AppColors.textColor,
),
children: [
WidgetSpan(
child: GestureDetector(
onTap: () {
AppState appState = getIt.get<AppState>();
Utils.openWebView(
url: appState.isArabic() ? AppUrls.hmgPrivacyUrlAr : AppUrls.hmgPrivacyUrlEn,
);
},
child: Text(
LocaleKeys.privacyPolicyText.tr(context: context),
style: TextStyle(
fontSize: 16.f,
fontFamily: getIt.get<AppState>().isArabic() ? 'CairoArabic' : 'Poppins',
fontWeight: FontWeight.w500,
color: AppColors.primaryRedColor,
decorationColor: AppColors.primaryRedColor,
),
),
),
),
],
),
),
),
],
),
),
),
],
).paddingAll(24.w),
),
),
_buildStickyBottomCard(context),
],
),
);
}
Widget _buildStickyBottomCard(BuildContext context) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: CustomButton(
text: LocaleKeys.next.tr(context: context),
onPressed: _canProceed ? () => context.navigateWithName(AppRoutes.userInfoSelection) : null,
isDisabled: !_canProceed,
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16.f,
),
),
],
),
SizedBox(height: 24.h),
],
).paddingSymmetrical(24.w, 0),
);
}
}

@ -17,14 +17,14 @@ 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:provider/provider.dart';
class UserInfoSelectionPage extends StatefulWidget {
const UserInfoSelectionPage({super.key});
class UserInfoSelectionScreen extends StatefulWidget {
const UserInfoSelectionScreen({super.key});
@override
State<UserInfoSelectionPage> createState() => _UserInfoSelectionPageState();
State<UserInfoSelectionScreen> createState() => _UserInfoSelectionScreenState();
}
class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
class _UserInfoSelectionScreenState extends State<UserInfoSelectionScreen> {
@override
void initState() {
super.initState();
@ -202,10 +202,15 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
padding: EdgeInsets.symmetric(vertical: 24.h, horizontal: 16.w),
child: Column(
children: [
LocaleKeys.helloIsYourInformationUpToDate.tr(namedArgs: {'name': name}).toText16(
weight: FontWeight.w600,
color: AppColors.textColor,
),
LocaleKeys.helloIsYourInformationUpToDate
.tr(namedArgs: {'name': name})
.toText16(
weight: FontWeight.w600,
color: AppColors.textColor,
)
.onPress(() {
context.navigateWithName(AppRoutes.userConsentScreen);
}),
SizedBox(height: 32.h),
_buildEditInfoTile(
context: context,

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/features/my_invoices/models/get_invoices_list_response_model.dart';
import 'package:hmg_patient_app_new/features/refund_request/models/resp_models/refund_request_list_response_model.dart';
import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/authentication/register.dart';
import 'package:hmg_patient_app_new/presentation/authentication/register_step2.dart';
@ -24,7 +25,6 @@ import 'package:hmg_patient_app_new/presentation/my_family/my_family.dart';
import 'package:hmg_patient_app_new/presentation/parking/paking_page.dart';
import 'package:hmg_patient_app_new/presentation/refund_request/refund_request_create_page.dart';
import 'package:hmg_patient_app_new/presentation/refund_request/refund_request_detail_screen.dart';
import 'package:hmg_patient_app_new/features/refund_request/models/resp_models/refund_request_list_response_model.dart';
import 'package:hmg_patient_app_new/presentation/service_price/services_price_list_page.dart';
import 'package:hmg_patient_app_new/presentation/smartwatches/smartwatch_home_page.dart';
import 'package:hmg_patient_app_new/presentation/symptoms_checker/organ_selector_screen.dart';
@ -33,8 +33,9 @@ import 'package:hmg_patient_app_new/presentation/symptoms_checker/risk_factors_s
import 'package:hmg_patient_app_new/presentation/symptoms_checker/suggestions_screen.dart';
import 'package:hmg_patient_app_new/presentation/symptoms_checker/symptoms_selector_screen.dart';
import 'package:hmg_patient_app_new/presentation/symptoms_checker/triage_screen.dart';
import 'package:hmg_patient_app_new/presentation/symptoms_checker/user_info_selection.dart';
import 'package:hmg_patient_app_new/presentation/symptoms_checker/user_consent_screen.dart';
import 'package:hmg_patient_app_new/presentation/symptoms_checker/user_info_selection/user_info_flow_manager.dart';
import 'package:hmg_patient_app_new/presentation/symptoms_checker/user_info_selection_screen.dart';
import 'package:hmg_patient_app_new/presentation/tele_consultation/zoom/call_screen.dart';
import 'package:hmg_patient_app_new/presentation/vital_sign/vital_sign_page.dart';
import 'package:hmg_patient_app_new/presentation/water_monitor/water_consumption_page.dart';
@ -85,6 +86,7 @@ class AppRoutes {
static const String possibleConditionsPage = '/possibleConditionsScreen';
static const String triagePage = '/triageProgressScreen';
static const String userInfoSelection = '/userInfoSelection';
static const String userConsentScreen = '/userConsentScreen';
static const String userInfoFlowManager = '/userInfoFlowManager';
static const String monthlyReports = '/monthlyReportsPage';
static const String qrParking = '/qrParkingPage';
@ -132,7 +134,8 @@ class AppRoutes {
bloodDonationPage: (context) => BloodDonationPage(),
myChildVaccine: (context) => MyChildVaccinePage(),
bookAppointmentPage: (context) => BookAppointmentPage(),
userInfoSelection: (context) => UserInfoSelectionPage(),
userInfoSelection: (context) => UserInfoSelectionScreen(),
userConsentScreen: (context) => UserConsentScreen(),
userInfoFlowManager: (context) => UserInfoFlowManager(),
smartWatches: (context) => SmartwatchHomePage(),
// huaweiHealthExample: (context) => HuaweiHealthExample(),

@ -177,3 +177,4 @@ flutter:
# SA8345000000815089206001
Loading…
Cancel
Save