Merge pull request 'Added Arabic Support for symptoms checker (faiz_dev)' (#165) from faiz_dev into master

Reviewed-on: https://34.17.182.140/Haroon6138/HMG_Patient_App_New/pulls/165
pull/168/head
Haroon6138 1 month ago
commit cf7ff4ce1d

@ -1377,6 +1377,18 @@
"suggestions": "الاقتراحات", "suggestions": "الاقتراحات",
"pleaseGoBackAndSelectOrgansFirst": "يرجى العودة واختيار الأعضاء أولاً", "pleaseGoBackAndSelectOrgansFirst": "يرجى العودة واختيار الأعضاء أولاً",
"symptomsSelector": "محدد الأعراض", "symptomsSelector": "محدد الأعراض",
"possibleSymptomsRelatedTo": "الأعراض المحتملة المتعلقة بـ",
"suggestionAIListDescription": "هذه قائمة بالأعراض المقترحة من قبل الذكاء الاصطناعي، بناءً على المعلومات التي تم جمعها حتى الآن خلال المقابلة",
"analyzing": "جاري التحليل...",
"seekMedicalAdvice": "اطلب المشورة الطبية",
"monitorDescription": "لا حاجة لطلب المشورة الطبية. فقط حافظ على روتين صحي.",
"seekMedicalAdviceDescription": "ليست حالة طوارئ ولكن من الأفضل مراقبة الأعراض.",
"emergencyDescription": "تحتاج إلى استشارة الطبيب في أقرب وقت ممكن قبل فوات الأوان.",
"colorScience": "دلالات الألوان",
"noNeedToVisitDoctor": "لا حاجة لزي<D8B2><D98A>رة الطبيب",
"commonSymptom": "الأعراض الشائعة",
"bookAppointment": "احجز موعد",
"notApplicable": "لا ينطبق",
"emergencyTriage": "طوارئ", "emergencyTriage": "طوارئ",
"emergencyEvidenceDetected": "تم اكتشاف دليل طوارئ. يرجى طلب العناية الطبية.", "emergencyEvidenceDetected": "تم اكتشاف دليل طوارئ. يرجى طلب العناية الطبية.",
"noQuestionItemsAvailable": "لا توجد عناصر أسئلة متاحة", "noQuestionItemsAvailable": "لا توجد عناصر أسئلة متاحة",

@ -1367,6 +1367,18 @@
"suggestions": "Suggestions", "suggestions": "Suggestions",
"pleaseGoBackAndSelectOrgansFirst": "Please go back and select organs first", "pleaseGoBackAndSelectOrgansFirst": "Please go back and select organs first",
"symptomsSelector": "Symptoms Selector", "symptomsSelector": "Symptoms Selector",
"possibleSymptomsRelatedTo": "Possible symptoms related to",
"suggestionAIListDescription": "This is a list of symptoms suggested by our AI, based on the information gathered so far during the interview",
"analyzing": "Analyzing...",
"seekMedicalAdvice": "Seek Medical Advice",
"monitorDescription": "No need to seek medical advice. Just keep healthy routine.",
"seekMedicalAdviceDescription": "Not emergency but better to monitor the symptoms.",
"emergencyDescription": "Need to consult doctor as soon as possible before getting too late.",
"colorScience": "Color Science",
"noNeedToVisitDoctor": "No need to visit doctor",
"commonSymptom": "Common Symptom",
"bookAppointment": "Book Appointment",
"notApplicable": "Not Applicable",
"emergencyTriage": "Emergency", "emergencyTriage": "Emergency",
"emergencyEvidenceDetected": "Emergency evidence detected. Please seek medical attention.", "emergencyEvidenceDetected": "Emergency evidence detected. Please seek medical attention.",
"noQuestionItemsAvailable": "No question items available", "noQuestionItemsAvailable": "No question items available",

@ -1,73 +1,73 @@
import 'dart:convert'; // import 'dart:convert';
import 'dart:io'; // import 'dart:io';
//
void main() async { // void main() async {
final file = File('assets/json/body_symptoms_data.json'); // final file = File('assets/json/body_symptoms_data.json');
final content = await file.readAsString(); // final content = await file.readAsString();
//
print('File size: ${content.length} characters'); // print('File size: ${content.length} characters');
//
// Split into two parts // // Split into two parts
final parts = content.split('export const SymptomsData = ['); // final parts = content.split('export const SymptomsData = [');
if (parts.length != 2) { // if (parts.length != 2) {
print('ERROR: Could not split file properly'); // print('ERROR: Could not split file properly');
exit(1); // exit(1);
} // }
//
print('Split into ${parts.length} parts'); // print('Split into ${parts.length} parts');
//
// Process BodySymptomsData // // Process BodySymptomsData
var bodyPart = parts[0] // var bodyPart = parts[0]
.replaceAll('export const BodySymptomsData = ', '') // .replaceAll('export const BodySymptomsData = ', '')
.trim() // .trim()
.replaceAll(RegExp(r';$'), ''); // .replaceAll(RegExp(r';$'), '');
//
print('Parsing body symptoms...'); // print('Parsing body symptoms...');
Map<String, dynamic> bodySymptomsData; // Map<String, dynamic> bodySymptomsData;
try { // try {
bodySymptomsData = json.decode(bodyPart); // bodySymptomsData = json.decode(bodyPart);
print('✅ Body symptoms parsed: ${bodySymptomsData.length} body parts'); // print('✅ Body symptoms parsed: ${bodySymptomsData.length} body parts');
} catch (e) { // } catch (e) {
print('❌ Error parsing body symptoms: $e'); // print('❌ Error parsing body symptoms: $e');
exit(1); // exit(1);
} // }
//
// Process SymptomsData // // Process SymptomsData
print('Processing symptoms data...'); // print('Processing symptoms data...');
var symptomsPart = parts[1] // var symptomsPart = parts[1]
.trim() // .trim()
.replaceAll(RegExp(r'\];$'), '') // .replaceAll(RegExp(r'\];$'), '')
.replaceAllMapped(RegExp(r'\n(\s*)id:'), (m) => '\n${m.group(1)}"id":') // .replaceAllMapped(RegExp(r'\n(\s*)id:'), (m) => '\n${m.group(1)}"id":')
.replaceAllMapped(RegExp(r'\n(\s*)type:'), (m) => '\n${m.group(1)}"type":') // .replaceAllMapped(RegExp(r'\n(\s*)type:'), (m) => '\n${m.group(1)}"type":')
.replaceAllMapped(RegExp(r'\n(\s*)name:'), (m) => '\n${m.group(1)}"name":') // .replaceAllMapped(RegExp(r'\n(\s*)name:'), (m) => '\n${m.group(1)}"name":')
.replaceAllMapped(RegExp(r'\n(\s*)common_name:'), (m) => '\n${m.group(1)}"common_name":'); // .replaceAllMapped(RegExp(r'\n(\s*)common_name:'), (m) => '\n${m.group(1)}"common_name":');
//
print('Parsing symptoms...'); // print('Parsing symptoms...');
List<dynamic> symptomsData; // List<dynamic> symptomsData;
try { // try {
symptomsData = json.decode('[$symptomsPart]'); // symptomsData = json.decode('[$symptomsPart]');
print('✅ Symptoms parsed: ${symptomsData.length} symptoms'); // print('✅ Symptoms parsed: ${symptomsData.length} symptoms');
} catch (e) { // } catch (e) {
print('❌ Error parsing symptoms: $e'); // print('❌ Error parsing symptoms: $e');
print('First 1000 chars:'); // print('First 1000 chars:');
print('[$symptomsPart]'.substring(0, 1000)); // print('[$symptomsPart]'.substring(0, 1000));
exit(1); // exit(1);
} // }
//
// Create final JSON // // Create final JSON
print('Creating final JSON structure...'); // print('Creating final JSON structure...');
final jsonStructure = { // final jsonStructure = {
'bodySymptoms': bodySymptomsData, // 'bodySymptoms': bodySymptomsData,
'symptoms': symptomsData, // 'symptoms': symptomsData,
}; // };
//
// Write to file // // Write to file
print('Writing to file...'); // print('Writing to file...');
final encoder = JsonEncoder.withIndent(' '); // final encoder = JsonEncoder.withIndent(' ');
await file.writeAsString(encoder.convert(jsonStructure)); // await file.writeAsString(encoder.convert(jsonStructure));
//
print('\n✅ SUCCESS! File converted to proper JSON'); // print('\n✅ SUCCESS! File converted to proper JSON');
print('✅ Body parts: ${bodySymptomsData.length}'); // print('✅ Body parts: ${bodySymptomsData.length}');
print('✅ Symptoms: ${symptomsData.length}'); // print('✅ Symptoms: ${symptomsData.length}');
} // }
//

@ -189,7 +189,7 @@ class ApiClientImp implements ApiClient {
// body['SessionID'] = "45786230487560q"; // body['SessionID'] = "45786230487560q";
} }
body['IsNewFlutterApp'] = true; // body['IsNewFlutterApp'] = true;
body.removeWhere((key, value) => value == null); body.removeWhere((key, value) => value == null);
} }

@ -8,28 +8,36 @@ class OrganData {
OrganModel( OrganModel(
id: 'head', id: 'head',
name: 'whole_head', name: 'whole_head',
nameAr: 'الرأس_كامل',
description: 'Head', description: 'Head',
descriptionAr: 'الرأس',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.50, y: 0.03), position: OrganPosition(x: 0.50, y: 0.03),
), ),
OrganModel( OrganModel(
id: 'left_eye', id: 'left_eye',
name: 'eyes', name: 'eyes',
nameAr: 'العيون',
description: 'Left Eye', description: 'Left Eye',
descriptionAr: 'العين اليسرى',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.43, y: 0.07), position: OrganPosition(x: 0.43, y: 0.07),
), ),
OrganModel( OrganModel(
id: 'right_eye', id: 'right_eye',
name: 'eyes', name: 'eyes',
nameAr: 'العيون',
description: 'Right Eye', description: 'Right Eye',
descriptionAr: 'العين اليمنى',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.57, y: 0.07), position: OrganPosition(x: 0.57, y: 0.07),
), ),
OrganModel( OrganModel(
id: 'nose_mouth', id: 'nose_mouth',
name: 'oral_cavity', name: 'oral_cavity',
nameAr: 'تجويف_الفم',
description: 'Nose/Mouth', description: 'Nose/Mouth',
descriptionAr: 'الأنف/الفم',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.50, y: 0.09), position: OrganPosition(x: 0.50, y: 0.09),
), ),
@ -38,7 +46,9 @@ class OrganData {
OrganModel( OrganModel(
id: 'throat', id: 'throat',
name: 'neck_or_throat', name: 'neck_or_throat',
nameAr: 'الرقبة_أو_الحلق',
description: 'Throat', description: 'Throat',
descriptionAr: 'الحلق',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.50, y: 0.15), position: OrganPosition(x: 0.50, y: 0.15),
), ),
@ -47,14 +57,18 @@ class OrganData {
OrganModel( OrganModel(
id: 'left_shoulder', id: 'left_shoulder',
name: 'upper_arm', name: 'upper_arm',
nameAr: 'الذراع_العلوي',
description: 'Left Shoulder', description: 'Left Shoulder',
descriptionAr: 'الكتف الأيسر',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.22, y: 0.22), position: OrganPosition(x: 0.22, y: 0.22),
), ),
OrganModel( OrganModel(
id: 'right_shoulder', id: 'right_shoulder',
name: 'upper_arm', name: 'upper_arm',
nameAr: 'الذراع_العلوي',
description: 'Right Shoulder', description: 'Right Shoulder',
descriptionAr: 'الكتف الأيمن',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.8, y: 0.22), position: OrganPosition(x: 0.8, y: 0.22),
), ),
@ -63,21 +77,27 @@ class OrganData {
OrganModel( OrganModel(
id: 'left_chest', id: 'left_chest',
name: 'chest', name: 'chest',
nameAr: 'الصدر',
description: 'Left Chest', description: 'Left Chest',
descriptionAr: 'الصدر الأيسر',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.38, y: 0.22), position: OrganPosition(x: 0.38, y: 0.22),
), ),
OrganModel( OrganModel(
id: 'center_chest', id: 'center_chest',
name: 'chest', name: 'chest',
nameAr: 'الصدر',
description: 'Center Chest', description: 'Center Chest',
descriptionAr: 'وسط الصدر',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.52, y: 0.25), position: OrganPosition(x: 0.52, y: 0.25),
), ),
OrganModel( OrganModel(
id: 'right_chest', id: 'right_chest',
name: 'chest', name: 'chest',
nameAr: 'الصدر',
description: 'Right Chest', description: 'Right Chest',
descriptionAr: 'الصدر الأيمن',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.65, y: 0.22), position: OrganPosition(x: 0.65, y: 0.22),
), ),
@ -86,14 +106,18 @@ class OrganData {
OrganModel( OrganModel(
id: 'left_ribs', id: 'left_ribs',
name: 'chest', name: 'chest',
nameAr: 'الصدر',
description: 'Left Ribs', description: 'Left Ribs',
descriptionAr: 'الأضلاع اليسرى',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.38, y: 0.35), position: OrganPosition(x: 0.38, y: 0.35),
), ),
OrganModel( OrganModel(
id: 'right_ribs', id: 'right_ribs',
name: 'chest', name: 'chest',
nameAr: 'الصدر',
description: 'Right Ribs', description: 'Right Ribs',
descriptionAr: 'الأضلاع اليمنى',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.64, y: 0.35), position: OrganPosition(x: 0.64, y: 0.35),
), ),
@ -102,21 +126,27 @@ class OrganData {
OrganModel( OrganModel(
id: 'upper_abdomen', id: 'upper_abdomen',
name: 'upper_abdomen', name: 'upper_abdomen',
nameAr: 'البطن_العلوي',
description: 'Upper Abdomen', description: 'Upper Abdomen',
descriptionAr: 'أعلى البطن',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.51, y: 0.31), position: OrganPosition(x: 0.51, y: 0.31),
), ),
OrganModel( OrganModel(
id: 'navel', id: 'navel',
name: 'mid_abdomen', name: 'mid_abdomen',
nameAr: 'البطن_الأوسط',
description: 'Navel', description: 'Navel',
descriptionAr: 'السرة',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.51, y: 0.38), position: OrganPosition(x: 0.51, y: 0.38),
), ),
OrganModel( OrganModel(
id: 'lower_abdomen', id: 'lower_abdomen',
name: 'lower_abdomen', name: 'lower_abdomen',
nameAr: 'البطن_السفلي',
description: 'Lower Abdomen', description: 'Lower Abdomen',
descriptionAr: 'أسفل البطن',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.51, y: 0.44), position: OrganPosition(x: 0.51, y: 0.44),
), ),
@ -125,14 +155,18 @@ class OrganData {
OrganModel( OrganModel(
id: 'left_groin', id: 'left_groin',
name: 'sexual_organs', name: 'sexual_organs',
nameAr: 'الأعضاء_التناسلية',
description: 'Left Groin', description: 'Left Groin',
descriptionAr: 'الفخذ الأيسر',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.42, y: 0.49), position: OrganPosition(x: 0.42, y: 0.49),
), ),
OrganModel( OrganModel(
id: 'right_groin', id: 'right_groin',
name: 'sexual_organs', name: 'sexual_organs',
nameAr: 'الأعضاء_التناسلية',
description: 'Right Groin', description: 'Right Groin',
descriptionAr: 'الفخذ الأيمن',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.59, y: 0.49), position: OrganPosition(x: 0.59, y: 0.49),
), ),
@ -142,21 +176,27 @@ class OrganData {
OrganModel( OrganModel(
id: 'left_elbow', id: 'left_elbow',
name: 'elbow', name: 'elbow',
nameAr: 'الكوع',
description: 'Left Elbow', description: 'Left Elbow',
descriptionAr: 'الكوع الأيسر',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.21, y: 0.35), position: OrganPosition(x: 0.21, y: 0.35),
), ),
OrganModel( OrganModel(
id: 'left_forearm', id: 'left_forearm',
name: 'forearm', name: 'forearm',
nameAr: 'الساعد',
description: 'Left Forearm', description: 'Left Forearm',
descriptionAr: 'الساعد الأيسر',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.19, y: 0.43), position: OrganPosition(x: 0.19, y: 0.43),
), ),
OrganModel( OrganModel(
id: 'left_wrist', id: 'left_wrist',
name: 'hand', name: 'hand',
nameAr: 'اليد',
description: 'Left Wrist', description: 'Left Wrist',
descriptionAr: 'المعصم الأيسر',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.14, y: 0.5), position: OrganPosition(x: 0.14, y: 0.5),
), ),
@ -166,21 +206,27 @@ class OrganData {
OrganModel( OrganModel(
id: 'right_elbow', id: 'right_elbow',
name: 'elbow', name: 'elbow',
nameAr: 'الكوع',
description: 'Right Elbow', description: 'Right Elbow',
descriptionAr: 'الكوع الأيمن',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.79, y: 0.35), position: OrganPosition(x: 0.79, y: 0.35),
), ),
OrganModel( OrganModel(
id: 'right_forearm', id: 'right_forearm',
name: 'forearm', name: 'forearm',
nameAr: 'الساعد',
description: 'Right Forearm', description: 'Right Forearm',
descriptionAr: 'الساعد الأيمن',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.81, y: 0.43), position: OrganPosition(x: 0.81, y: 0.43),
), ),
OrganModel( OrganModel(
id: 'right_wrist', id: 'right_wrist',
name: 'hand', name: 'hand',
nameAr: 'اليد',
description: 'Right Wrist', description: 'Right Wrist',
descriptionAr: 'المعصم الأيمن',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.85, y: 0.5), position: OrganPosition(x: 0.85, y: 0.5),
), ),
@ -189,28 +235,36 @@ class OrganData {
OrganModel( OrganModel(
id: 'left_thigh', id: 'left_thigh',
name: 'thigh', name: 'thigh',
nameAr: 'الفخذ',
description: 'Left Thigh', description: 'Left Thigh',
descriptionAr: 'الفخذ الأيسر',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.4, y: 0.60), position: OrganPosition(x: 0.4, y: 0.60),
), ),
OrganModel( OrganModel(
id: 'left_knee', id: 'left_knee',
name: 'knee', name: 'knee',
nameAr: 'الركبة',
description: 'Left Knee', description: 'Left Knee',
descriptionAr: 'الركبة اليسرى',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.4, y: 0.72), position: OrganPosition(x: 0.4, y: 0.72),
), ),
OrganModel( OrganModel(
id: 'left_shin', id: 'left_shin',
name: 'lower_leg', name: 'lower_leg',
nameAr: 'الساق_السفلي',
description: 'Left Shin', description: 'Left Shin',
descriptionAr: 'الساق اليسرى',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.41, y: 0.82), position: OrganPosition(x: 0.41, y: 0.82),
), ),
OrganModel( OrganModel(
id: 'left_ankle', id: 'left_ankle',
name: 'foot', name: 'foot',
nameAr: 'القدم',
description: 'Left Ankle', description: 'Left Ankle',
descriptionAr: 'الكاحل الأيسر',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.43, y: 0.91), position: OrganPosition(x: 0.43, y: 0.91),
), ),
@ -219,28 +273,36 @@ class OrganData {
OrganModel( OrganModel(
id: 'right_thigh', id: 'right_thigh',
name: 'thigh', name: 'thigh',
nameAr: 'الفخذ',
description: 'Right Thigh', description: 'Right Thigh',
descriptionAr: 'الفخذ الأيمن',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.65, y: 0.60), position: OrganPosition(x: 0.65, y: 0.60),
), ),
OrganModel( OrganModel(
id: 'right_knee', id: 'right_knee',
name: 'knee', name: 'knee',
nameAr: 'الركبة',
description: 'Right Knee', description: 'Right Knee',
descriptionAr: 'الركبة اليمنى',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.6, y: 0.72), position: OrganPosition(x: 0.6, y: 0.72),
), ),
OrganModel( OrganModel(
id: 'right_shin', id: 'right_shin',
name: 'lower_leg', name: 'lower_leg',
nameAr: 'الساق_السفلي',
description: 'Right Shin', description: 'Right Shin',
descriptionAr: 'الساق اليمنى',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.57, y: 0.82), position: OrganPosition(x: 0.57, y: 0.82),
), ),
OrganModel( OrganModel(
id: 'right_ankle', id: 'right_ankle',
name: 'foot', name: 'foot',
nameAr: 'القدم',
description: 'Right Ankle', description: 'Right Ankle',
descriptionAr: 'الكاحل الأيمن',
bodyView: BodyView.front, bodyView: BodyView.front,
position: OrganPosition(x: 0.57, y: 0.91), position: OrganPosition(x: 0.57, y: 0.91),
), ),
@ -252,14 +314,18 @@ class OrganData {
OrganModel( OrganModel(
id: 'back_of_head', id: 'back_of_head',
name: 'whole_head', name: 'whole_head',
nameAr: 'الرأس_كامل',
description: 'Back of Head', description: 'Back of Head',
descriptionAr: 'مؤخرة الرأس',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.50, y: 0.04), position: OrganPosition(x: 0.50, y: 0.04),
), ),
OrganModel( OrganModel(
id: 'neck_back', id: 'neck_back',
name: 'nape_of_neck', name: 'nape_of_neck',
nameAr: 'مؤخرة_الرقبة',
description: 'Neck', description: 'Neck',
descriptionAr: 'الرقبة',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.50, y: 0.14), position: OrganPosition(x: 0.50, y: 0.14),
), ),
@ -268,14 +334,18 @@ class OrganData {
OrganModel( OrganModel(
id: 'left_shoulder_back', id: 'left_shoulder_back',
name: 'upper_arm', name: 'upper_arm',
nameAr: 'الذراع_العلوي',
description: 'Left Shoulder', description: 'Left Shoulder',
descriptionAr: 'الكتف الأيسر',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.33, y: 0.19), position: OrganPosition(x: 0.33, y: 0.19),
), ),
OrganModel( OrganModel(
id: 'right_shoulder_back', id: 'right_shoulder_back',
name: 'upper_arm', name: 'upper_arm',
nameAr: 'الذراع_العلوي',
description: 'Right Shoulder', description: 'Right Shoulder',
descriptionAr: 'الكتف الأيمن',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.67, y: 0.19), position: OrganPosition(x: 0.67, y: 0.19),
), ),
@ -284,21 +354,27 @@ class OrganData {
OrganModel( OrganModel(
id: 'upper_spine', id: 'upper_spine',
name: 'back', name: 'back',
nameAr: 'الظهر',
description: 'Upper Spine', description: 'Upper Spine',
descriptionAr: 'العمود الفقري العلوي',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.50, y: 0.23), position: OrganPosition(x: 0.50, y: 0.23),
), ),
OrganModel( OrganModel(
id: 'left_upper_back', id: 'left_upper_back',
name: 'back', name: 'back',
nameAr: 'الظهر',
description: 'Left Upper Back', description: 'Left Upper Back',
descriptionAr: 'أعلى الظهر الأيسر',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.38, y: 0.28), position: OrganPosition(x: 0.38, y: 0.28),
), ),
OrganModel( OrganModel(
id: 'right_upper_back', id: 'right_upper_back',
name: 'back', name: 'back',
nameAr: 'الظهر',
description: 'Right Upper Back', description: 'Right Upper Back',
descriptionAr: 'أعلى الظهر الأيمن',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.62, y: 0.28), position: OrganPosition(x: 0.62, y: 0.28),
), ),
@ -307,21 +383,27 @@ class OrganData {
OrganModel( OrganModel(
id: 'mid_spine', id: 'mid_spine',
name: 'back', name: 'back',
nameAr: 'الظهر',
description: 'Mid Spine', description: 'Mid Spine',
descriptionAr: 'العمود الفقري الأوسط',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.50, y: 0.35), position: OrganPosition(x: 0.50, y: 0.35),
), ),
OrganModel( OrganModel(
id: 'left_mid_back', id: 'left_mid_back',
name: 'back', name: 'back',
nameAr: 'الظهر',
description: 'Left Mid Back', description: 'Left Mid Back',
descriptionAr: 'وسط الظهر الأيسر',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.38, y: 0.35), position: OrganPosition(x: 0.38, y: 0.35),
), ),
OrganModel( OrganModel(
id: 'right_mid_back', id: 'right_mid_back',
name: 'back', name: 'back',
nameAr: 'الظهر',
description: 'Right Mid Back', description: 'Right Mid Back',
descriptionAr: 'وسط الظهر الأيمن',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.62, y: 0.35), position: OrganPosition(x: 0.62, y: 0.35),
), ),
@ -330,21 +412,27 @@ class OrganData {
OrganModel( OrganModel(
id: 'lower_spine', id: 'lower_spine',
name: 'lower_back', name: 'lower_back',
nameAr: 'أسفل_الظهر',
description: 'Lower Back', description: 'Lower Back',
descriptionAr: 'أسفل الظهر',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.50, y: 0.43), position: OrganPosition(x: 0.50, y: 0.43),
), ),
OrganModel( OrganModel(
id: 'left_lower_back', id: 'left_lower_back',
name: 'lower_back', name: 'lower_back',
nameAr: 'أسفل_الظهر',
description: 'Left Lower Back', description: 'Left Lower Back',
descriptionAr: 'أسفل الظهر الأيسر',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.40, y: 0.43), position: OrganPosition(x: 0.40, y: 0.43),
), ),
OrganModel( OrganModel(
id: 'right_lower_back', id: 'right_lower_back',
name: 'lower_back', name: 'lower_back',
nameAr: 'أسفل_الظهر',
description: 'Right Lower Back', description: 'Right Lower Back',
descriptionAr: 'أسفل الظهر الأيمن',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.60, y: 0.43), position: OrganPosition(x: 0.60, y: 0.43),
), ),
@ -353,21 +441,27 @@ class OrganData {
OrganModel( OrganModel(
id: 'sacrum', id: 'sacrum',
name: 'lower_back', name: 'lower_back',
nameAr: 'أسفل_الظهر',
description: 'Sacrum', description: 'Sacrum',
descriptionAr: 'العجز',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.50, y: 0.52), position: OrganPosition(x: 0.50, y: 0.52),
), ),
OrganModel( OrganModel(
id: 'left_glute', id: 'left_glute',
name: 'buttocks', name: 'buttocks',
nameAr: 'الأرداف',
description: 'Left Glute', description: 'Left Glute',
descriptionAr: 'الأرداف اليسرى',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.45, y: 0.56), position: OrganPosition(x: 0.45, y: 0.56),
), ),
OrganModel( OrganModel(
id: 'right_glute', id: 'right_glute',
name: 'buttocks', name: 'buttocks',
nameAr: 'الأرداف',
description: 'Right Glute', description: 'Right Glute',
descriptionAr: 'الأرداف اليمنى',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.55, y: 0.56), position: OrganPosition(x: 0.55, y: 0.56),
), ),
@ -376,28 +470,36 @@ class OrganData {
OrganModel( OrganModel(
id: 'left_hamstring', id: 'left_hamstring',
name: 'thigh', name: 'thigh',
nameAr: 'الفخذ',
description: 'Left Hamstring', description: 'Left Hamstring',
descriptionAr: 'عضلة الفخذ الخلفية اليسرى',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.43, y: 0.65), position: OrganPosition(x: 0.43, y: 0.65),
), ),
OrganModel( OrganModel(
id: 'right_hamstring', id: 'right_hamstring',
name: 'thigh', name: 'thigh',
nameAr: 'الفخذ',
description: 'Right Hamstring', description: 'Right Hamstring',
descriptionAr: 'عضلة الفخذ الخلفية اليمنى',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.57, y: 0.65), position: OrganPosition(x: 0.57, y: 0.65),
), ),
OrganModel( OrganModel(
id: 'left_calf', id: 'left_calf',
name: 'lower_leg', name: 'lower_leg',
nameAr: 'الساق_السفلي',
description: 'Left Calf', description: 'Left Calf',
descriptionAr: 'ربلة الساق اليسرى',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.43, y: 0.79), position: OrganPosition(x: 0.43, y: 0.79),
), ),
OrganModel( OrganModel(
id: 'right_calf', id: 'right_calf',
name: 'lower_leg', name: 'lower_leg',
nameAr: 'الساق_السفلي',
description: 'Right Calf', description: 'Right Calf',
descriptionAr: 'ربلة الساق اليمنى',
bodyView: BodyView.back, bodyView: BodyView.back,
position: OrganPosition(x: 0.57, y: 0.79), position: OrganPosition(x: 0.57, y: 0.79),
), ),

@ -3,29 +3,47 @@ import 'package:hmg_patient_app_new/core/enums.dart';
class OrganModel { class OrganModel {
final String id; final String id;
final String name; final String name;
final String nameAr;
final String description; final String description;
final String descriptionAr;
final BodyView bodyView; final BodyView bodyView;
final OrganPosition position; final OrganPosition position;
const OrganModel({ const OrganModel({
required this.id, required this.id,
required this.name, required this.name,
required this.nameAr,
required this.description, required this.description,
required this.descriptionAr,
required this.bodyView, required this.bodyView,
required this.position, required this.position,
}); });
/// Get localized name based on isArabic flag
String getLocalizedName(bool isArabic) {
return isArabic ? nameAr : name;
}
/// Get localized description based on isArabic flag
String getLocalizedDescription(bool isArabic) {
return isArabic ? descriptionAr : description;
}
OrganModel copyWith({ OrganModel copyWith({
String? id, String? id,
String? name, String? name,
String? nameAr,
String? description, String? description,
String? descriptionAr,
BodyView? bodyView, BodyView? bodyView,
OrganPosition? position, OrganPosition? position,
}) { }) {
return OrganModel( return OrganModel(
id: id ?? this.id, id: id ?? this.id,
name: name ?? this.name, name: name ?? this.name,
nameAr: nameAr ?? this.nameAr,
description: description ?? this.description, description: description ?? this.description,
descriptionAr: descriptionAr ?? this.descriptionAr,
bodyView: bodyView ?? this.bodyView, bodyView: bodyView ?? this.bodyView,
position: position ?? this.position, position: position ?? this.position,
); );

@ -22,6 +22,8 @@ class RiskAndSuggestionsItemModel {
final String? type; final String? type;
final String? name; final String? name;
final String? commonName; final String? commonName;
final String? nameAr;
final String? commonNameAr;
final String? language; final String? language;
RiskAndSuggestionsItemModel({ RiskAndSuggestionsItemModel({
@ -29,6 +31,8 @@ class RiskAndSuggestionsItemModel {
this.type, this.type,
this.name, this.name,
this.commonName, this.commonName,
this.nameAr,
this.commonNameAr,
this.language, this.language,
}); });
@ -38,6 +42,8 @@ class RiskAndSuggestionsItemModel {
type: json['type'], type: json['type'],
name: json['name'], name: json['name'],
commonName: json['common_name'], commonName: json['common_name'],
nameAr: json['nameAr'],
commonNameAr: json['common_nameAr'],
language: json['language'], language: json['language'],
); );
} }
@ -48,12 +54,17 @@ class RiskAndSuggestionsItemModel {
'type': type, 'type': type,
'name': name, 'name': name,
'common_name': commonName, 'common_name': commonName,
'nameAr': nameAr,
'common_nameAr': commonNameAr,
'language': language, 'language': language,
}; };
} }
// Helper method to get display name // Helper method to get display name based on locale
String getDisplayName() { String getDisplayName({bool isArabic = false}) {
if (isArabic) {
return commonNameAr ?? nameAr ?? commonName ?? name ?? '';
}
return commonName ?? name ?? ''; return commonName ?? name ?? '';
} }
} }

@ -172,6 +172,8 @@ class TriageCondition {
final String? id; final String? id;
final String? name; final String? name;
final String? commonName; final String? commonName;
final String? nameAr;
final String? commonNameAr;
final double? probability; final double? probability;
final ConditionDetails? conditionDetails; final ConditionDetails? conditionDetails;
@ -179,6 +181,8 @@ class TriageCondition {
this.id, this.id,
this.name, this.name,
this.commonName, this.commonName,
this.nameAr,
this.commonNameAr,
this.probability, this.probability,
this.conditionDetails, this.conditionDetails,
}); });
@ -188,6 +192,8 @@ class TriageCondition {
id: json['id'], id: json['id'],
name: json['name'], name: json['name'],
commonName: json['common_name'], commonName: json['common_name'],
nameAr: json['nameAr'],
commonNameAr: json['common_nameAr'],
probability: json['probability']?.toDouble(), probability: json['probability']?.toDouble(),
conditionDetails: json['condition_details'] != null ? ConditionDetails.fromJson(json['condition_details']) : null, conditionDetails: json['condition_details'] != null ? ConditionDetails.fromJson(json['condition_details']) : null,
); );
@ -198,11 +204,21 @@ class TriageCondition {
'id': id, 'id': id,
'name': name, 'name': name,
'common_name': commonName, 'common_name': commonName,
'nameAr': nameAr,
'common_nameAr': commonNameAr,
'probability': probability, 'probability': probability,
'condition_details': conditionDetails?.toJson(), 'condition_details': conditionDetails?.toJson(),
}; };
} }
/// Get display name based on locale
String getDisplayName({bool isArabic = false}) {
if (isArabic) {
return commonNameAr ?? nameAr ?? commonName ?? name ?? '';
}
return commonName ?? name ?? '';
}
/// Get probability as percentage /// Get probability as percentage
String getProbabilityPercentage() { String getProbabilityPercentage() {
if (probability == null) return '0%'; if (probability == null) return '0%';

@ -23,6 +23,23 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
required this.appState, required this.appState,
}); });
// Gender constants for both English and Arabic
static const String _maleEn = "male";
static const String _femaleEn = "female";
static const String _maleAr = "ذكر";
static const String _femaleAr = "أنثى";
/// Convert gender string to integer (1 for male, 2 for female)
/// Handles both English and Arabic values
int _getGenderAsInt(String? gender) {
if (gender == null) return 1; // Default to male
final lowerGender = gender.toLowerCase();
if (lowerGender == _maleEn || gender == _maleAr) {
return 1;
}
return 2; // Female or any other value
}
// State variables // State variables
bool isBodyHidden = false; bool isBodyHidden = false;
BodyView _currentView = BodyView.front; BodyView _currentView = BodyView.front;
@ -440,7 +457,7 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
await getRiskFactors( await getRiskFactors(
age: _selectedAge!, age: _selectedAge!,
gender: _selectedGender!.toLowerCase() == "male" ? 1 : 2, gender: _getGenderAsInt(_selectedGender),
evidenceIds: evidenceIds, evidenceIds: evidenceIds,
sessionId: currentSessionId, sessionId: currentSessionId,
language: appState.isArabic() ? 'ar' : 'en', language: appState.isArabic() ? 'ar' : 'en',
@ -498,6 +515,8 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
id: "not_applicable", id: "not_applicable",
commonName: "Not Applicable", commonName: "Not Applicable",
name: "Not Applicable", name: "Not Applicable",
commonNameAr: "لا ينطبق",
nameAr: "لا ينطبق",
language: appState.isArabic() ? 'ar' : 'en', language: appState.isArabic() ? 'ar' : 'en',
type: null, type: null,
); );
@ -612,7 +631,7 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
await getSuggestions( await getSuggestions(
age: _selectedAge!, age: _selectedAge!,
gender: _selectedGender!.toLowerCase() == "male" ? 1 : 2, gender: _getGenderAsInt(_selectedGender),
evidenceIds: evidenceIds, evidenceIds: evidenceIds,
language: appState.isArabic() ? 'ar' : 'en', language: appState.isArabic() ? 'ar' : 'en',
onSuccess: (response) { onSuccess: (response) {
@ -668,6 +687,8 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
id: "not_applicable", id: "not_applicable",
commonName: "Not Applicable", commonName: "Not Applicable",
name: "Not Applicable", name: "Not Applicable",
commonNameAr: "لا ينطبق",
nameAr: "لا ينطبق",
language: appState.isArabic() ? 'ar' : 'en', language: appState.isArabic() ? 'ar' : 'en',
type: null, type: null,
); );
@ -764,7 +785,7 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
await getDiagnosisForTriage( await getDiagnosisForTriage(
age: _selectedAge!, age: _selectedAge!,
gender: _selectedGender!.toLowerCase() == "male" ? 1 : 2, gender: _getGenderAsInt(_selectedGender),
evidenceIds: evidenceIds, evidenceIds: evidenceIds,
language: appState.isArabic() ? 'ar' : 'en', language: appState.isArabic() ? 'ar' : 'en',
onSuccess: (response) { onSuccess: (response) {
@ -927,6 +948,22 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
bool get isUserInfoFirstPage => _userInfoCurrentPage == 0; bool get isUserInfoFirstPage => _userInfoCurrentPage == 0;
/// Check if current user info page has valid selection
bool get isCurrentUserInfoPageValid {
switch (_userInfoCurrentPage) {
case 0: // Gender page
return _selectedGender != null && _selectedGender!.isNotEmpty;
case 1: // Age/Birth date page
return _dateOfBirth != null || (_selectedAge != null && _selectedAge! > 0);
case 2: // Height page
return _selectedHeight > 0;
case 3: // Weight page
return _selectedWeight > 0;
default:
return false;
}
}
/// Validate and submit user info /// Validate and submit user info
Map<String, dynamic> getUserInfoData() { Map<String, dynamic> getUserInfoData() {
return { return {
@ -988,7 +1025,7 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
final result = await symptomsCheckerRepo.getBodySymptomsByName( final result = await symptomsCheckerRepo.getBodySymptomsByName(
organNames: organNames, organNames: organNames,
userSessionToken: currentSessionAuthToken, userSessionToken: currentSessionAuthToken,
gender: (selectedGender ?? "Male").toLowerCase() == "male" ? 1 : 2, gender: _getGenderAsInt(selectedGender ?? "Male"),
); );
result.fold( result.fold(

@ -1371,6 +1371,17 @@ abstract class LocaleKeys {
static const suggestions = 'suggestions'; static const suggestions = 'suggestions';
static const pleaseGoBackAndSelectOrgansFirst = 'pleaseGoBackAndSelectOrgansFirst'; static const pleaseGoBackAndSelectOrgansFirst = 'pleaseGoBackAndSelectOrgansFirst';
static const symptomsSelector = 'symptomsSelector'; static const symptomsSelector = 'symptomsSelector';
static const possibleSymptomsRelatedTo = 'possibleSymptomsRelatedTo';
static const suggestionAIListDescription = 'suggestionAIListDescription';
static const analyzing = 'analyzing';
static const seekMedicalAdvice = 'seekMedicalAdvice';
static const monitorDescription = 'monitorDescription';
static const seekMedicalAdviceDescription = 'seekMedicalAdviceDescription';
static const emergencyDescription = 'emergencyDescription';
static const colorScience = 'colorScience';
static const noNeedToVisitDoctor = 'noNeedToVisitDoctor';
static const commonSymptom = 'commonSymptom';
static const notApplicable = 'notApplicable';
static const emergencyTriage = 'emergencyTriage'; static const emergencyTriage = 'emergencyTriage';
static const emergencyEvidenceDetected = 'emergencyEvidenceDetected'; static const emergencyEvidenceDetected = 'emergencyEvidenceDetected';
static const noQuestionItemsAvailable = 'noQuestionItemsAvailable'; static const noQuestionItemsAvailable = 'noQuestionItemsAvailable';

@ -7,7 +7,6 @@ import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/utils.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/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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_view_model.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/generated/locale_keys.g.dart';
@ -44,7 +43,9 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
); );
return; return;
} }
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.pleaseWait.tr(context: context),); LoaderBottomSheet.showLoader(
loadingText: LocaleKeys.pleaseWait.tr(context: context),
);
final String userName = 'guest_user'; final String userName = 'guest_user';
final String password = '123456'; final String password = '123456';
@ -102,7 +103,7 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
width: 32.h, width: 32.h,
height: 32.h, height: 32.h,
), ),
padding: const EdgeInsets.only(left: 12), padding: EdgeInsetsDirectional.only(start: 12),
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
highlightColor: Colors.transparent, highlightColor: Colors.transparent,
), ),
@ -125,21 +126,32 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
} }
Widget _buildBodyViewer(SymptomsCheckerViewModel viewModel) { Widget _buildBodyViewer(SymptomsCheckerViewModel viewModel) {
return Padding( return GestureDetector(
padding: EdgeInsets.fromLTRB(16.h, 16.h, 16.h, 60.h), onHorizontalDragEnd: (details) {
child: AnimatedSwitcher( // Swipe left or right to toggle view
duration: const Duration(milliseconds: 600), if (details.primaryVelocity != null) {
transitionBuilder: (child, animation) => _build3DFlipTransition(child, animation), if (details.primaryVelocity! < -200 || details.primaryVelocity! > 200) {
switchInCurve: Curves.easeInOut, viewModel.toggleView();
switchOutCurve: Curves.easeInOut, }
child: InteractiveBodyWidget( }
key: ValueKey<BodyView>(viewModel.currentView), },
bodyImageAsset: viewModel.currentView == BodyView.front ? AppAssets.fullBodyFront : AppAssets.fullBodyBack, child: Padding(
organs: viewModel.currentOrgans, padding: EdgeInsets.fromLTRB(16.h, 16.h, 16.h, 60.h),
selectedOrganIds: viewModel.selectedOrganIds, child: AnimatedSwitcher(
onOrganTap: viewModel.toggleOrganSelection, duration: const Duration(milliseconds: 600),
isBodyHidden: viewModel.isBodyHidden, transitionBuilder: (child, animation) => _build3DFlipTransition(child, animation),
tooltipOrganId: viewModel.tooltipOrganId, switchInCurve: Curves.easeInOut,
switchOutCurve: Curves.easeInOut,
child: InteractiveBodyWidget(
key: ValueKey<BodyView>(viewModel.currentView),
bodyImageAsset: viewModel.currentView == BodyView.front ? AppAssets.fullBodyFront : AppAssets.fullBodyBack,
organs: viewModel.currentOrgans,
selectedOrganIds: viewModel.selectedOrganIds,
onOrganTap: viewModel.toggleOrganSelection,
isBodyHidden: viewModel.isBodyHidden,
tooltipOrganId: viewModel.tooltipOrganId,
isArabic: _appState.isArabic(),
),
), ),
), ),
); );
@ -169,8 +181,8 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
} }
Widget _buildViewToggleButtons(SymptomsCheckerViewModel viewModel) { Widget _buildViewToggleButtons(SymptomsCheckerViewModel viewModel) {
return Positioned( return PositionedDirectional(
left: 16.w, start: 16.w,
top: 36.h, top: 36.h,
child: Column( child: Column(
children: [ children: [
@ -275,6 +287,8 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
); );
} }
final isArabic = _appState.isArabic();
return ConstrainedBox( return ConstrainedBox(
constraints: BoxConstraints(maxHeight: 150.h), constraints: BoxConstraints(maxHeight: 150.h),
child: SingleChildScrollView( child: SingleChildScrollView(
@ -284,7 +298,7 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
runSpacing: 8.h, runSpacing: 8.h,
children: viewModel.selectedOrgans.map((organ) { children: viewModel.selectedOrgans.map((organ) {
return AppCustomChipWidget( return AppCustomChipWidget(
labelText: organ.description, labelText: organ.getLocalizedDescription(isArabic),
backgroundColor: AppColors.secondaryLightRedColor, backgroundColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor, textColor: AppColors.primaryRedColor,
deleteIcon: AppAssets.cancel, deleteIcon: AppAssets.cancel,
@ -313,8 +327,8 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
} }
Widget _buildExpandCollapseButton(SymptomsCheckerViewModel viewModel) { Widget _buildExpandCollapseButton(SymptomsCheckerViewModel viewModel) {
return Positioned( return PositionedDirectional(
right: 24.w, end: 24.w,
top: -24.h, top: -24.h,
child: GestureDetector( child: GestureDetector(
onTap: viewModel.toggleBottomSheet, onTap: viewModel.toggleBottomSheet,
@ -331,13 +345,16 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
borderRadius: 11.r, borderRadius: 11.r,
), ),
child: Center( child: Center(
child: AnimatedRotation( child: Transform.flip(
duration: const Duration(milliseconds: 300), flipX: _appState.isArabic(),
turns: viewModel.isBottomSheetExpanded ? 0.25 : -0.25, child: AnimatedRotation(
child: Utils.buildSvgWithAssets( duration: const Duration(milliseconds: 300),
icon: AppAssets.arrowRight, turns: viewModel.isBottomSheetExpanded ? 0.25 : -0.25,
width: 25.w, child: Utils.buildSvgWithAssets(
height: 25.h, icon: AppAssets.arrowRight,
width: 25.w,
height: 25.h,
),
), ),
), ),
), ),

@ -108,25 +108,25 @@ class PossibleConditionsPage extends StatelessWidget {
); );
} }
Widget _buildInfoTile({required PossibleConditionsSeverityEnum severityEnum}) { Widget _buildInfoTile({required PossibleConditionsSeverityEnum severityEnum, required BuildContext context}) {
String title; String title;
String description; String description;
Color dotColor; Color dotColor;
switch (severityEnum) { switch (severityEnum) {
case PossibleConditionsSeverityEnum.monitorOnly: case PossibleConditionsSeverityEnum.monitorOnly:
title = "Monitor".needTranslation; title = LocaleKeys.monitor.tr(context: context);
description = "No need to seek medical advice. Just keep healthy routine.".needTranslation; description = LocaleKeys.monitorDescription.tr(context: context);
dotColor = AppColors.chipColorMonitor; dotColor = AppColors.chipColorMonitor;
break; break;
case PossibleConditionsSeverityEnum.seekMedicalAdvice: case PossibleConditionsSeverityEnum.seekMedicalAdvice:
title = "Seek Medical Advice".needTranslation; title = LocaleKeys.seekMedicalAdvice.tr(context: context);
description = "Not emergency but better to monitor the symptoms.".needTranslation; description = LocaleKeys.seekMedicalAdviceDescription.tr(context: context);
dotColor = AppColors.chipColorSeekMedicalAdvice; dotColor = AppColors.chipColorSeekMedicalAdvice;
break; break;
case PossibleConditionsSeverityEnum.emergency: case PossibleConditionsSeverityEnum.emergency:
title = "Emergency".needTranslation; title = LocaleKeys.emergency.tr(context: context);
description = "Need to consult doctor as soon as possible before getting too late.".needTranslation; description = LocaleKeys.emergencyDescription.tr(context: context);
dotColor = AppColors.chipColorEmergency; dotColor = AppColors.chipColorEmergency;
break; break;
} }
@ -156,7 +156,7 @@ class PossibleConditionsPage extends StatelessWidget {
_buildSeverityDetailsBottomsheet({required BuildContext context}) { _buildSeverityDetailsBottomsheet({required BuildContext context}) {
return showCommonBottomSheetWithoutHeight( return showCommonBottomSheetWithoutHeight(
title: "Color Science".needTranslation, title: LocaleKeys.colorScience.tr(context: context),
context, context,
child: Container( child: Container(
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
@ -164,11 +164,11 @@ class PossibleConditionsPage extends StatelessWidget {
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
child: Column( child: Column(
children: [ children: [
_buildInfoTile(severityEnum: PossibleConditionsSeverityEnum.monitorOnly), _buildInfoTile(severityEnum: PossibleConditionsSeverityEnum.monitorOnly, context: context),
Divider(color: AppColors.bottomNAVBorder, height: 24.h, thickness: 1), Divider(color: AppColors.bottomNAVBorder, height: 24.h, thickness: 1),
_buildInfoTile(severityEnum: PossibleConditionsSeverityEnum.seekMedicalAdvice), _buildInfoTile(severityEnum: PossibleConditionsSeverityEnum.seekMedicalAdvice, context: context),
Divider(color: AppColors.bottomNAVBorder, height: 24.h, thickness: 1), Divider(color: AppColors.bottomNAVBorder, height: 24.h, thickness: 1),
_buildInfoTile(severityEnum: PossibleConditionsSeverityEnum.emergency), _buildInfoTile(severityEnum: PossibleConditionsSeverityEnum.emergency, context: context),
], ],
), ),
), ),

@ -3,6 +3,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.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/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.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/route_extensions.dart';
@ -25,11 +26,13 @@ class RiskFactorsScreen extends StatefulWidget {
class _RiskFactorsScreenState extends State<RiskFactorsScreen> { class _RiskFactorsScreenState extends State<RiskFactorsScreen> {
late DialogService dialogService; late DialogService dialogService;
late AppState _appState;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
dialogService = getIt<DialogService>(); dialogService = getIt<DialogService>();
_appState = getIt<AppState>();
// Fetch risk factors based on selected symptoms // Fetch risk factors based on selected symptoms
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
final viewModel = context.read<SymptomsCheckerViewModel>(); final viewModel = context.read<SymptomsCheckerViewModel>();
@ -90,6 +93,8 @@ class _RiskFactorsScreenState extends State<RiskFactorsScreen> {
} }
Widget _buildRiskFactorsList(SymptomsCheckerViewModel viewModel) { Widget _buildRiskFactorsList(SymptomsCheckerViewModel viewModel) {
final isArabic = _appState.isArabic();
return Container( return Container(
key: ValueKey<int>(viewModel.riskFactorsList.length), key: ValueKey<int>(viewModel.riskFactorsList.length),
width: double.infinity, width: double.infinity,
@ -100,7 +105,7 @@ class _RiskFactorsScreenState extends State<RiskFactorsScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
...viewModel.riskFactorsList.map((factor) { ...viewModel.riskFactorsList.map((factor) {
return _buildRiskFactorItem(viewModel, factor.id ?? '', factor.getDisplayName()); return _buildRiskFactorItem(viewModel, factor.id ?? '', factor.getDisplayName(isArabic: isArabic));
}), }),
SizedBox(height: 12.w), SizedBox(height: 12.w),
Row( Row(

@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.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/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.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/route_extensions.dart';
@ -24,11 +25,13 @@ class SuggestionsScreen extends StatefulWidget {
class _SuggestionsScreenState extends State<SuggestionsScreen> { class _SuggestionsScreenState extends State<SuggestionsScreen> {
late DialogService dialogService; late DialogService dialogService;
late AppState _appState;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
dialogService = getIt<DialogService>(); dialogService = getIt<DialogService>();
_appState = getIt<AppState>();
// Initialize symptom groups based on selected organs // Initialize symptom groups based on selected organs
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
final viewModel = context.read<SymptomsCheckerViewModel>(); final viewModel = context.read<SymptomsCheckerViewModel>();
@ -92,6 +95,8 @@ class _SuggestionsScreenState extends State<SuggestionsScreen> {
} }
Widget _buildSuggestionsList(SymptomsCheckerViewModel viewModel) { Widget _buildSuggestionsList(SymptomsCheckerViewModel viewModel) {
final isArabic = _appState.isArabic();
return Container( return Container(
key: ValueKey<int>(viewModel.suggestionsList.length), key: ValueKey<int>(viewModel.suggestionsList.length),
width: double.infinity, width: double.infinity,
@ -102,7 +107,7 @@ class _SuggestionsScreenState extends State<SuggestionsScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
...viewModel.suggestionsList.map((factor) { ...viewModel.suggestionsList.map((factor) {
return _buildSuggestionItem(viewModel, factor.id ?? '', factor.getDisplayName()); return _buildSuggestionItem(viewModel, factor.id ?? '', factor.getDisplayName(isArabic: isArabic));
}), }),
SizedBox(height: 12.w), SizedBox(height: 12.w),
Row( Row(
@ -115,7 +120,7 @@ class _SuggestionsScreenState extends State<SuggestionsScreen> {
), ),
SizedBox(width: 12.w), SizedBox(width: 12.w),
Expanded( Expanded(
child: "This is a list of symptoms suggested by our AI, based on the information gathered so far during the interview".toText12( child: LocaleKeys.suggestionAIListDescription.tr(context: context).toText12(
color: AppColors.greyInfoTextColor, color: AppColors.greyInfoTextColor,
), ),
) )

@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.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/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.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/route_extensions.dart';
@ -26,11 +27,13 @@ class SymptomsSelectorPage extends StatefulWidget {
class _SymptomsSelectorPageState extends State<SymptomsSelectorPage> { class _SymptomsSelectorPageState extends State<SymptomsSelectorPage> {
late DialogService dialogService; late DialogService dialogService;
late AppState _appState;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
dialogService = getIt<DialogService>(); dialogService = getIt<DialogService>();
_appState = getIt<AppState>();
// Initialize symptom groups based on selected organs // Initialize symptom groups based on selected organs
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
final viewModel = context.read<SymptomsCheckerViewModel>(); final viewModel = context.read<SymptomsCheckerViewModel>();
@ -100,10 +103,11 @@ class _SymptomsSelectorPageState extends State<SymptomsSelectorPage> {
// Find matching organ ID from selected organs // Find matching organ ID from selected organs
String? organId; String? organId;
String? organName; String? organName;
final isArabic = _appState.isArabic();
for (var organ in viewModel.selectedOrgans) { for (var organ in viewModel.selectedOrgans) {
if (organ.name == organResult.name) { if (organ.name == organResult.name) {
organId = organ.id; organId = organ.id;
organName = organ.description; organName = organ.getLocalizedDescription(isArabic);
break; break;
} }
} }
@ -141,6 +145,8 @@ class _SymptomsSelectorPageState extends State<SymptomsSelectorPage> {
String organName, String organName,
List<BodySymptom> symptoms, List<BodySymptom> symptoms,
) { ) {
final isArabic = _appState.isArabic();
return Container( return Container(
width: double.infinity, width: double.infinity,
margin: EdgeInsets.symmetric(horizontal: 24.w), margin: EdgeInsets.symmetric(horizontal: 24.w),
@ -154,7 +160,7 @@ class _SymptomsSelectorPageState extends State<SymptomsSelectorPage> {
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
'Possible symptoms related to "$organName"', '${LocaleKeys.possibleSymptomsRelatedTo.tr(context: context)} "$organName"',
style: TextStyle(fontSize: 18.f, fontWeight: FontWeight.w600, color: AppColors.textColor), style: TextStyle(fontSize: 18.f, fontWeight: FontWeight.w600, color: AppColors.textColor),
), ),
), ),
@ -166,7 +172,7 @@ class _SymptomsSelectorPageState extends State<SymptomsSelectorPage> {
spacing: 8.w, spacing: 8.w,
children: symptoms.map((symptom) { children: symptoms.map((symptom) {
final symptomId = symptom.id ?? ''; final symptomId = symptom.id ?? '';
final symptomLabel = symptom.getDisplayName(false); // Pass isArabic from AppState if needed final symptomLabel = symptom.getDisplayName(isArabic);
bool isSelected = viewModel.isSymptomSelected(organId, symptomId); bool isSelected = viewModel.isSymptomSelected(organId, symptomId);
return GestureDetector( return GestureDetector(
onTap: () => viewModel.toggleSymptomSelection(organId, symptomId), onTap: () => viewModel.toggleSymptomSelection(organId, symptomId),
@ -306,7 +312,7 @@ class _SymptomsSelectorPageState extends State<SymptomsSelectorPage> {
fontSize: 16.f, fontSize: 16.f,
), ),
), ),
], ],
), ),
SizedBox(height: 24.h), SizedBox(height: 24.h),
], ],

@ -4,6 +4,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.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/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.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/route_extensions.dart';
@ -32,12 +33,14 @@ class TriagePage extends StatefulWidget {
class _TriagePageState extends State<TriagePage> { class _TriagePageState extends State<TriagePage> {
late SymptomsCheckerViewModel viewModel; late SymptomsCheckerViewModel viewModel;
late DialogService dialogService; late DialogService dialogService;
late AppState _appState;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
viewModel = context.read<SymptomsCheckerViewModel>(); viewModel = context.read<SymptomsCheckerViewModel>();
dialogService = getIt.get<DialogService>(); dialogService = getIt.get<DialogService>();
_appState = getIt.get<AppState>();
// Start triage process when screen loads // Start triage process when screen loads
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
@ -436,7 +439,8 @@ class _TriagePageState extends State<TriagePage> {
Widget _buildStickyBottomCard(BuildContext context, SymptomsCheckerViewModel viewModel) { Widget _buildStickyBottomCard(BuildContext context, SymptomsCheckerViewModel viewModel) {
// Get the top condition with highest probability // Get the top condition with highest probability
final conditions = viewModel.currentConditions ?? []; final conditions = viewModel.currentConditions ?? [];
String suggestedCondition = "Analyzing..."; final isArabic = _appState.isArabic();
String suggestedCondition = LocaleKeys.analyzing.tr(context: context);
double probability = 0.0; double probability = 0.0;
if (conditions.isNotEmpty) { if (conditions.isNotEmpty) {
@ -445,7 +449,7 @@ class _TriagePageState extends State<TriagePage> {
sortedConditions.sort((a, b) => (b.probability ?? 0.0).compareTo(a.probability ?? 0.0)); sortedConditions.sort((a, b) => (b.probability ?? 0.0).compareTo(a.probability ?? 0.0));
final topCondition = sortedConditions.first; final topCondition = sortedConditions.first;
suggestedCondition = topCondition.commonName ?? topCondition.name ?? "Unknown"; suggestedCondition = topCondition.getDisplayName(isArabic: isArabic);
probability = (topCondition.probability ?? 0.0) * 100; // Convert to percentage probability = (topCondition.probability ?? 0.0) * 100; // Convert to percentage
} }
// final bool isHighConfidence = probability >= 70.0; // final bool isHighConfidence = probability >= 70.0;

@ -43,13 +43,10 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
if (user == null) return; if (user == null) return;
// Populate gender (gender is int: 1=Male, 2=Female) // Populate gender (gender is int: 1=Male, 2=Female)
// Use internal keys (male/female) for storage
if (user.gender != null) { if (user.gender != null) {
String genderStr = user.gender == 1 String genderKey = user.gender == 1 ? "male" : user.gender == 2 ? "female" : "other";
? "Male" viewModel.setGender(genderKey);
: user.gender == 2
? "Female"
: "Other";
viewModel.setGender(genderStr);
} }
if (user.dateofBirth != null && user.dateofBirth!.isNotEmpty) { if (user.dateofBirth != null && user.dateofBirth!.isNotEmpty) {
@ -84,12 +81,13 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
child: Row( child: Row(
children: [ children: [
Container( Container(
height: 40.h, height: 40.h,
width: 40.h, width: 40.h,
margin: EdgeInsets.only(right: 10.h), margin: EdgeInsetsDirectional.only(end: 10.h),
padding: EdgeInsets.all(8.h), padding: EdgeInsets.all(8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 12.r, color: AppColors.greyColor), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 12.r, color: AppColors.greyColor),
child: Utils.buildSvgWithAssets(icon: leadingIcon, iconColor: iconColor)), child: Utils.buildSvgWithAssets(icon: leadingIcon, iconColor: iconColor),
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -112,6 +110,20 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
).paddingSymmetrical(0, 8.h); ).paddingSymmetrical(0, 8.h);
} }
/// Convert internal gender key to localized display string
String _getLocalizedGender(String? genderKey, BuildContext context) {
if (genderKey == null) return LocaleKeys.notSet.tr(context: context);
final lowerKey = genderKey.toLowerCase();
if (lowerKey == "male") {
return LocaleKeys.malE.tr(context: context);
} else if (lowerKey == "female") {
return LocaleKeys.femaleGender.tr(context: context);
} else {
return genderKey; // Return as-is if not recognized
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>(); AppState appState = getIt.get<AppState>();
@ -151,7 +163,7 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
viewModel.selectedWeight == null; viewModel.selectedWeight == null;
// Get display values // Get display values
String genderText = viewModel.selectedGender ?? LocaleKeys.notSet.tr(context: context); String genderText = _getLocalizedGender(viewModel.selectedGender, context);
// Show age calculated from DOB (prefer viewModel's age, fallback to calculated from user's DOB) // Show age calculated from DOB (prefer viewModel's age, fallback to calculated from user's DOB)
int? displayAge = viewModel.selectedAge ?? userAgeFromDOB; int? displayAge = viewModel.selectedAge ?? userAgeFromDOB;
String ageText = displayAge != null ? "$displayAge ${LocaleKeys.years.tr(context: context)}" : LocaleKeys.notSet.tr(context: context); String ageText = displayAge != null ? "$displayAge ${LocaleKeys.years.tr(context: context)}" : LocaleKeys.notSet.tr(context: context);

@ -15,12 +15,16 @@ class GenderSelectionPage extends StatelessWidget {
final String? selectedGender; final String? selectedGender;
final Function(String) onGenderSelected; final Function(String) onGenderSelected;
GenderSelectionPage({ const GenderSelectionPage({
super.key, super.key,
required this.selectedGender, required this.selectedGender,
required this.onGenderSelected, required this.onGenderSelected,
}); });
// Internal gender keys (constant, not localized)
static const String maleKey = "male";
static const String femaleKey = "female";
_buildGenderOption(String iconPng, String label, bool isSelected) { _buildGenderOption(String iconPng, String label, bool isSelected) {
return Container( return Container(
height: 160.h, height: 160.h,
@ -40,7 +44,27 @@ class GenderSelectionPage extends StatelessWidget {
); );
} }
final genders = ["Male", "Female"]; /// Check if the selected gender matches the given key
/// Handles both internal keys and localized strings
bool _isGenderSelected(String genderKey, String? currentSelection, BuildContext context) {
if (currentSelection == null) return false;
final lowerSelection = currentSelection.toLowerCase();
if (genderKey == maleKey) {
// Check against internal key, English, and Arabic
return lowerSelection == maleKey ||
lowerSelection == "male" ||
currentSelection == LocaleKeys.malE.tr(context: context) ||
currentSelection == "ذكر";
} else {
// Check against internal key, English, and Arabic
return lowerSelection == femaleKey ||
lowerSelection == "female" ||
currentSelection == LocaleKeys.femaleGender.tr(context: context) ||
currentSelection == "أنثى";
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -56,16 +80,25 @@ class GenderSelectionPage extends StatelessWidget {
children: [ children: [
Expanded( Expanded(
child: InkWell( child: InkWell(
onTap: () => onGenderSelected(genders[0]), onTap: () => onGenderSelected(maleKey),
child: _buildGenderOption(AppAssets.maleIcon, LocaleKeys.malE.tr(context: context), symptomsViewModel.selectedGender == genders[0]), child: _buildGenderOption(
AppAssets.maleIcon,
LocaleKeys.malE.tr(context: context),
_isGenderSelected(maleKey, symptomsViewModel.selectedGender, context),
),
), ),
), ),
SizedBox(width: 16.w), SizedBox(width: 16.w),
Expanded( Expanded(
child: InkWell( child: InkWell(
onTap: () => onGenderSelected(genders[1]), onTap: () => onGenderSelected(femaleKey),
child: _buildGenderOption(AppAssets.femaleIcon, LocaleKeys.femaleGender.tr(context: context), symptomsViewModel.selectedGender == genders[1]), child: _buildGenderOption(
)) AppAssets.femaleIcon,
LocaleKeys.femaleGender.tr(context: context),
_isGenderSelected(femaleKey, symptomsViewModel.selectedGender, context),
),
),
)
], ],
), ),
], ],

@ -4,7 +4,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/extensions/route_extensions.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/features/symptoms_checker/symptoms_checker_view_model.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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/symptoms_checker/user_info_selection/pages/age_selection_page.dart'; import 'package:hmg_patient_app_new/presentation/symptoms_checker/user_info_selection/pages/age_selection_page.dart';
@ -29,13 +28,15 @@ class _UserInfoFlowManagerState extends State<UserInfoFlowManager> {
late PageController _pageController; late PageController _pageController;
late SymptomsCheckerViewModel _viewModel; late SymptomsCheckerViewModel _viewModel;
// Page titles // Get localized page titles
final List<String> _pageTitles = [ List<String> _getPageTitles(BuildContext context) {
"Your Gender", return [
"Your Birth Date", LocaleKeys.genderSymptoms.tr(context: context),
"Your Height", LocaleKeys.dateOfBirthSymptoms.tr(context: context),
"Your Weight", LocaleKeys.heightSymptoms.tr(context: context),
]; LocaleKeys.weightSymptoms.tr(context: context),
];
}
@override @override
void initState() { void initState() {
@ -187,9 +188,11 @@ class _UserInfoFlowManagerState extends State<UserInfoFlowManager> {
? // Single page edit mode - show only Save button ? // Single page edit mode - show only Save button
CustomButton( CustomButton(
text: LocaleKeys.save.tr(context: context), text: LocaleKeys.save.tr(context: context),
onPressed: _onNext, isDisabled: !(viewModel.isCurrentUserInfoPageValid),
backgroundColor: AppColors.primaryRedColor, onPressed: () => viewModel.isCurrentUserInfoPageValid ? _onNext() : () {},
borderColor: AppColors.primaryRedColor, backgroundColor:
viewModel.isCurrentUserInfoPageValid ? AppColors.primaryRedColor : AppColors.primaryRedColor.withValues(alpha: 0.5),
borderColor: viewModel.isCurrentUserInfoPageValid ? AppColors.primaryRedColor : AppColors.primaryRedColor.withValues(alpha: 0.5),
textColor: AppColors.whiteColor, textColor: AppColors.whiteColor,
fontSize: 16.f, fontSize: 16.f,
) )
@ -228,6 +231,8 @@ class _UserInfoFlowManagerState extends State<UserInfoFlowManager> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final pageTitles = _getPageTitles(context);
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: Column( body: Column(
@ -235,7 +240,7 @@ class _UserInfoFlowManagerState extends State<UserInfoFlowManager> {
Expanded( Expanded(
child: CollapsingListView( child: CollapsingListView(
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
title: _pageTitles[_viewModel.userInfoCurrentPage], title: pageTitles[_viewModel.userInfoCurrentPage],
isLeading: true, isLeading: true,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,

@ -2,6 +2,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.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/enums.dart'; import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/extensions/string_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/extensions/widget_extensions.dart';
@ -24,8 +26,11 @@ class ConditionCard extends StatelessWidget {
this.onActionPressed, this.onActionPressed,
}); });
/// Get title from condition /// Get title from condition (localized)
String get title => condition.commonName ?? condition.name ?? ''; String get title {
final appState = getIt<AppState>();
return condition.getDisplayName(isArabic: appState.isArabic());
}
/// Get percentage from probability (0-1 to 0-100) /// Get percentage from probability (0-1 to 0-100)
int get percentage => ((condition.probability ?? 0) * 100).toInt(); int get percentage => ((condition.probability ?? 0) * 100).toInt();
@ -69,14 +74,14 @@ class ConditionCard extends StatelessWidget {
return LocaleKeys.emergency.tr(context: context); return LocaleKeys.emergency.tr(context: context);
case TriageLevelEnum.consultation24: case TriageLevelEnum.consultation24:
case TriageLevelEnum.consultation: case TriageLevelEnum.consultation:
return "Seek Medical Advice".needTranslation; return LocaleKeys.seekMedicalAdvice.tr(context: context);
case TriageLevelEnum.selfCare: case TriageLevelEnum.selfCare:
return LocaleKeys.monitor.tr(context: context); return LocaleKeys.monitor.tr(context: context);
} }
} }
// Default based on probability // Default based on probability
if (percentage >= 70) { if (percentage >= 70) {
return "Seek Medical Advice".needTranslation; return LocaleKeys.seekMedicalAdvice.tr(context: context);
} else { } else {
return LocaleKeys.monitor.tr(context: context); return LocaleKeys.monitor.tr(context: context);
} }
@ -85,7 +90,7 @@ class ConditionCard extends StatelessWidget {
/// Get monitor note if applicable /// Get monitor note if applicable
String? getMonitorNote(BuildContext context) { String? getMonitorNote(BuildContext context) {
if (severityEnum == PossibleConditionsSeverityEnum.monitorOnly) { if (severityEnum == PossibleConditionsSeverityEnum.monitorOnly) {
return "No need to visit doctor".needTranslation; return LocaleKeys.noNeedToVisitDoctor.tr(context: context);
} }
return null; return null;
} }
@ -236,7 +241,7 @@ class ConditionCard extends StatelessWidget {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
"Common Symptom".needTranslation.toText14(weight: FontWeight.w600, color: AppColors.textColor), LocaleKeys.commonSymptom.tr(context: context).toText14(weight: FontWeight.w600, color: AppColors.textColor),
_buildSymptomsRow(), _buildSymptomsRow(),
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.description.tr(context: context).toText14(weight: FontWeight.bold, color: AppColors.textColor), LocaleKeys.description.tr(context: context).toText14(weight: FontWeight.bold, color: AppColors.textColor),
@ -244,7 +249,7 @@ class ConditionCard extends StatelessWidget {
description.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), description.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
if (severityEnum != PossibleConditionsSeverityEnum.monitorOnly) if (severityEnum != PossibleConditionsSeverityEnum.monitorOnly)
CustomButton( CustomButton(
text: "Book Appointment".needTranslation, text: LocaleKeys.bookAppointment.tr(context: context),
onPressed: () { onPressed: () {
if (onActionPressed != null) { if (onActionPressed != null) {
onActionPressed!(); onActionPressed!();

@ -13,6 +13,7 @@ class InteractiveBodyWidget extends StatefulWidget {
final bool isBodyHidden; final bool isBodyHidden;
final Function(String organId) onOrganTap; final Function(String organId) onOrganTap;
final String? tooltipOrganId; final String? tooltipOrganId;
final bool isArabic;
const InteractiveBodyWidget({ const InteractiveBodyWidget({
super.key, super.key,
@ -22,6 +23,7 @@ class InteractiveBodyWidget extends StatefulWidget {
required this.onOrganTap, required this.onOrganTap,
this.isBodyHidden = false, this.isBodyHidden = false,
this.tooltipOrganId, this.tooltipOrganId,
this.isArabic = false,
}); });
@override @override
@ -118,7 +120,10 @@ class _InteractiveBodyWidgetState extends State<InteractiveBodyWidget> {
(organ) { (organ) {
final centerX = organ.position.x * imageConstraints.maxWidth; final centerX = organ.position.x * imageConstraints.maxWidth;
final centerY = organ.position.y * imageConstraints.maxHeight; final centerY = organ.position.y * imageConstraints.maxHeight;
return OrganTooltipWidget(organName: organ.description, position: Offset(centerX, centerY)); return OrganTooltipWidget(
organName: organ.getLocalizedDescription(widget.isArabic),
position: Offset(centerX, centerY),
);
}, },
), ),
], ],

@ -74,15 +74,18 @@ class CustomButton extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
if (icon != null) if (icon != null)
Padding( Flexible(
padding: text.isNotEmpty ? EdgeInsets.only(right: 4.w, left: 4.w) : EdgeInsets.zero, child: Padding(
child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconS, height: iconS), padding: text.isNotEmpty ? EdgeInsets.only(right: 4.w, left: 4.w) : EdgeInsets.zero,
child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconS, height: iconS),
),
), ),
Visibility( if (text.isNotEmpty)
visible: text.isNotEmpty, Text(
child: Text(
text, text,
overflow: textOverflow, overflow: textOverflow ?? TextOverflow.ellipsis,
maxLines: 1,
textAlign: TextAlign.center,
style: context.dynamicTextStyle( style: context.dynamicTextStyle(
fontSize: fontS, fontSize: fontS,
color: isDisabled ? AppColors.greyTextColor : textColor, color: isDisabled ? AppColors.greyTextColor : textColor,
@ -90,7 +93,6 @@ class CustomButton extends StatelessWidget {
fontWeight: fontWeight, fontWeight: fontWeight,
), ),
), ),
),
], ],
), ),
)); ));

Loading…
Cancel
Save