Merge branch 'master' into faiz_dev

# Conflicts:
#	lib/presentation/hmg_services/services_page.dart
pull/138/head
faizatflutter 2 weeks ago
commit 460e32d25a

@ -722,7 +722,16 @@ class Utils {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset(AppAssets.mada, width: 25.h, height: 25.h),
Image.asset(AppAssets.tamaraEng, width: 25.h, height: 25.h),
Image.asset(
AppAssets.tamaraEng,
width: 25.h,
height: 25.h,
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
debugPrint('Failed to load Tamara PNG in payment methods: $error');
return Utils.buildSvgWithAssets(icon: AppAssets.tamara, width: 25.h, height: 25.h, fit: BoxFit.contain);
},
),
Image.asset(AppAssets.visa, width: 25.h, height: 25.h),
Image.asset(AppAssets.mastercard, width: 25.h, height: 25.h),
Image.asset(AppAssets.applePay, width: 25.h, height: 25.h),

@ -260,10 +260,10 @@ class AuthenticationRepoImp implements AuthenticationRepo {
newRequest.forRegisteration = newRequest.isRegister ?? false;
newRequest.isRegister = false;
//silent login case removed token and login token
// if(newRequest.logInTokenID.isEmpty && newRequest.isSilentLogin == true && (newRequest.loginType==1 || newRequest.loginType==4)) {
// newRequest.logInTokenID = null;
// newRequest.deviceToken = null;
// }
if(newRequest.logInTokenID.isEmpty && newRequest.isSilentLogin == true && (newRequest.loginType==1 || newRequest.loginType==4)) {
newRequest.logInTokenID = null;
newRequest.deviceToken = null;
}
}

@ -17,8 +17,11 @@ import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'models/req_models/create_e_referral_model.dart';
import 'models/req_models/send_activation_code_ereferral_req_model.dart';
import 'models/resq_models/covid_get_test_proceedure_resp.dart';
import 'models/resq_models/get_covid_payment_info_resp.dart';
import 'models/resq_models/relationship_type_resp_mode.dart';
import 'models/resq_models/search_e_referral_resp_model.dart';
import 'models/resq_models/vital_sign_respo_model.dart';
abstract class HmgServicesRepo {
Future<Either<Failure, GenericApiModel<List<GetCMCAllOrdersResponseModel>>>> getAllComprehensiveCheckupOrders();
@ -60,7 +63,11 @@ abstract class HmgServicesRepo {
Future<Either<Failure, GenericApiModel<List<SearchEReferralResponseModel>>>> searchEReferral(SearchEReferralRequestModel requestModel);
Future<Either<Failure, GenericApiModel<List<Covid19GetTestProceduresResp>>>> getCovidTestProcedures();
Future<Either<Failure, GenericApiModel<Covid19GetPaymentInfo>>> getCovidPaymentInfo(String procedureID, int projectID);
Future<Either<Failure, GenericApiModel<List<VitalSignResModel>>>> getPatientVitalSign();
}
class HmgServicesRepoImp implements HmgServicesRepo {
@ -816,4 +823,149 @@ class HmgServicesRepoImp implements HmgServicesRepo {
}
}
@override
Future<Either<Failure, GenericApiModel<List<Covid19GetTestProceduresResp>>>> getCovidTestProcedures() async {
try {
GenericApiModel<List<Covid19GetTestProceduresResp>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_COVID_DRIVETHRU_PROCEDURES_LIST,
body: {"TestTypeEnum":2,"TestProcedureEnum":3,},
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("Covid Test Procedure : $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<Covid19GetTestProceduresResp> covidTestProcedure = [];
if (response['COVID19_TestProceduresList'] != null && response['COVID19_TestProceduresList'] is List) {
final servicesList = response['COVID19_TestProceduresList'] as List;
for (var serviceJson in servicesList) {
if (serviceJson is Map<String, dynamic>) {
covidTestProcedure.add(Covid19GetTestProceduresResp.fromJson(serviceJson));
}
}
}
apiResponse = GenericApiModel<List<Covid19GetTestProceduresResp>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: covidTestProcedure,
);
} catch (e) {
loggerService.logError("Error parsing E-Referral services: ${e.toString()}");
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
log("Unknown error in Search Referral: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<Covid19GetPaymentInfo>>> getCovidPaymentInfo(String procedureID, int projectID) async {
try {
GenericApiModel<Covid19GetPaymentInfo>? apiResponse;
Failure? failure;
await apiClient.post(
GET_COVID_DRIVETHRU_PAYMENT_INFO,
body: {"TestTypeEnum":2,"TestProcedureEnum":3, "ProcedureId":procedureID, "ProjectID":projectID,},
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("Covid Test Procedure : $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
Covid19GetPaymentInfo covidPaymentInfo = Covid19GetPaymentInfo.fromJson(response["COVID19_PatientShare"]);
apiResponse = GenericApiModel<Covid19GetPaymentInfo>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: covidPaymentInfo,
);
} catch (e) {
loggerService.logError("Error parsing E-Referral services: ${e.toString()}");
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
log("Unknown error in Search Referral: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<VitalSignResModel>>>> getPatientVitalSign() async {
Map<String, dynamic> requestBody = {};
try {
GenericApiModel<List<VitalSignResModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_PATIENT_VITAL_SIGN,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("Patient Vital Sign API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<VitalSignResModel> vitalSignList = [];
if (response['PatientVitalSignList'] != null && response['PatientVitalSignList'] is List) {
final vitalSignsList = response['PatientVitalSignList'] as List;
for (var vitalSignJson in vitalSignsList) {
if (vitalSignJson is Map<String, dynamic>) {
vitalSignList.add(VitalSignResModel.fromJson(vitalSignJson));
}
}
}
apiResponse = GenericApiModel<List<VitalSignResModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: vitalSignList,
);
} catch (e) {
loggerService.logError("Error parsing Patient Vital Sign: ${e.toString()}");
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
log("Unknown error in getPatientVitalSign: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
}

@ -10,15 +10,18 @@ import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/crea
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/search_e_referral_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/send_activation_code_ereferral_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/covid_get_test_proceedure_resp.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/search_e_referral_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/vital_sign_respo_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'models/req_models/check_activation_e_referral_req_model.dart';
import 'models/resq_models/get_covid_payment_info_resp.dart';
import 'models/resq_models/relationship_type_resp_mode.dart';
import 'models/ui_models/covid_questionnare_model.dart';
@ -35,6 +38,7 @@ class HmgServicesViewModel extends ChangeNotifier {
bool isCmcServicesLoading = false;
bool isUpdatingOrder = false;
bool isHospitalListLoading = false;
bool isVitalSignLoading = false;
// HHC specific loading states
bool isHhcOrdersLoading = false;
@ -45,6 +49,7 @@ class HmgServicesViewModel extends ChangeNotifier {
List<HospitalsModel> hospitalsList = [];
List<HospitalsModel> filteredHospitalsList = [];
HospitalsModel? selectedHospital;
List<VitalSignResModel> vitalSignList = [];
// HHC specific lists
List<GetCMCAllOrdersResponseModel> hhcOrdersList = [];
@ -60,7 +65,8 @@ class HmgServicesViewModel extends ChangeNotifier {
List<GetAllRelationshipTypeResponseModel> relationTypes = [];
List<GetAllCitiesResponseModel> getAllCitiesList = [];
List<SearchEReferralResponseModel> searchReferralList = [];
List<Covid19GetTestProceduresResp> covidTestProcedureList = [];
Covid19GetPaymentInfo? covidPaymentInfo;
Future<void> getOrdersList() async {}
@ -783,4 +789,111 @@ class HmgServicesViewModel extends ChangeNotifier {
return <CovidQuestionnaireModel>[];
}
}
Future<void> getCovidProcedureList({
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
notifyListeners();
final result = await hmgServicesRepo.getCovidTestProcedures();
result.fold(
(failure) async {
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 1) {
covidTestProcedureList = apiResponse.data ?? [];
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<void> getPaymentInfo({
String? procedureID,
int? projectID,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
notifyListeners();
final result = await hmgServicesRepo.getCovidPaymentInfo(procedureID!, projectID!);
result.fold(
(failure) async {
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 1) {
covidPaymentInfo = apiResponse.data;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<void> getPatientVitalSign({
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
isVitalSignLoading = true;
notifyListeners();
final result = await hmgServicesRepo.getPatientVitalSign();
result.fold(
(failure) async {
isVitalSignLoading = false;
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
isVitalSignLoading = false;
if (apiResponse.messageStatus == 1) {
vitalSignList = apiResponse.data ?? [];
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
}

@ -0,0 +1,33 @@
import 'dart:convert';
class Covid19GetTestProceduresResp {
String? procedureId;
String? procedureName;
String? procedureNameN;
String? setupId;
Covid19GetTestProceduresResp({
this.procedureId,
this.procedureName,
this.procedureNameN,
this.setupId,
});
factory Covid19GetTestProceduresResp.fromRawJson(String str) => Covid19GetTestProceduresResp.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Covid19GetTestProceduresResp.fromJson(Map<String, dynamic> json) => Covid19GetTestProceduresResp(
procedureId: json["ProcedureID"],
procedureName: json["ProcedureName"],
procedureNameN: json["ProcedureNameN"],
setupId: json["SetupID"],
);
Map<String, dynamic> toJson() => {
"ProcedureID": procedureId,
"ProcedureName": procedureName,
"ProcedureNameN": procedureNameN,
"SetupID": setupId,
};
}

@ -0,0 +1,105 @@
import 'dart:convert';
class Covid19GetPaymentInfo {
dynamic propertyChanged;
int? cashPriceField;
int? cashPriceTaxField;
int? cashPriceWithTaxField;
int? companyIdField;
String? companyNameField;
int? companyShareWithTaxField;
dynamic errCodeField;
int? groupIdField;
dynamic insurancePolicyNoField;
String? messageField;
dynamic patientCardIdField;
int? patientShareField;
double? patientShareWithTaxField;
double? patientTaxAmountField;
int? policyIdField;
dynamic policyNameField;
dynamic procedureIdField;
String? procedureNameField;
dynamic setupIdField;
int? statusCodeField;
dynamic subPolicyNoField;
Covid19GetPaymentInfo({
this.propertyChanged,
this.cashPriceField,
this.cashPriceTaxField,
this.cashPriceWithTaxField,
this.companyIdField,
this.companyNameField,
this.companyShareWithTaxField,
this.errCodeField,
this.groupIdField,
this.insurancePolicyNoField,
this.messageField,
this.patientCardIdField,
this.patientShareField,
this.patientShareWithTaxField,
this.patientTaxAmountField,
this.policyIdField,
this.policyNameField,
this.procedureIdField,
this.procedureNameField,
this.setupIdField,
this.statusCodeField,
this.subPolicyNoField,
});
factory Covid19GetPaymentInfo.fromRawJson(String str) => Covid19GetPaymentInfo.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Covid19GetPaymentInfo.fromJson(Map<String, dynamic> json) => Covid19GetPaymentInfo(
propertyChanged: json["PropertyChanged"],
cashPriceField: json["cashPriceField"],
cashPriceTaxField: json["cashPriceTaxField"],
cashPriceWithTaxField: json["cashPriceWithTaxField"],
companyIdField: json["companyIdField"],
companyNameField: json["companyNameField"],
companyShareWithTaxField: json["companyShareWithTaxField"],
errCodeField: json["errCodeField"],
groupIdField: json["groupIDField"],
insurancePolicyNoField: json["insurancePolicyNoField"],
messageField: json["messageField"],
patientCardIdField: json["patientCardIDField"],
patientShareField: json["patientShareField"],
patientShareWithTaxField: json["patientShareWithTaxField"]?.toDouble(),
patientTaxAmountField: json["patientTaxAmountField"]?.toDouble(),
policyIdField: json["policyIdField"],
policyNameField: json["policyNameField"],
procedureIdField: json["procedureIdField"],
procedureNameField: json["procedureNameField"],
setupIdField: json["setupIDField"],
statusCodeField: json["statusCodeField"],
subPolicyNoField: json["subPolicyNoField"],
);
Map<String, dynamic> toJson() => {
"PropertyChanged": propertyChanged,
"cashPriceField": cashPriceField,
"cashPriceTaxField": cashPriceTaxField,
"cashPriceWithTaxField": cashPriceWithTaxField,
"companyIdField": companyIdField,
"companyNameField": companyNameField,
"companyShareWithTaxField": companyShareWithTaxField,
"errCodeField": errCodeField,
"groupIDField": groupIdField,
"insurancePolicyNoField": insurancePolicyNoField,
"messageField": messageField,
"patientCardIDField": patientCardIdField,
"patientShareField": patientShareField,
"patientShareWithTaxField": patientShareWithTaxField,
"patientTaxAmountField": patientTaxAmountField,
"policyIdField": policyIdField,
"policyNameField": policyNameField,
"procedureIdField": procedureIdField,
"procedureNameField": procedureNameField,
"setupIDField": setupIdField,
"statusCodeField": statusCodeField,
"subPolicyNoField": subPolicyNoField,
};
}

@ -0,0 +1,259 @@
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class VitalSignResModel {
var transNo;
var projectID;
var weightKg;
var heightCm;
var temperatureCelcius;
var pulseBeatPerMinute;
var respirationBeatPerMinute;
var bloodPressureLower;
var bloodPressureHigher;
var sAO2;
var fIO2;
var painScore;
var bodyMassIndex;
var headCircumCm;
var leanBodyWeightLbs;
var idealBodyWeightLbs;
var temperatureCelciusMethod;
var pulseRhythm;
var respirationPattern;
var bloodPressureCuffLocation;
var bloodPressureCuffSize;
var bloodPressurePatientPosition;
var painLocation;
var painDuration;
var painCharacter;
var painFrequency;
bool? isPainManagementDone;
var status;
bool? isVitalsRequired;
var patientID;
var createdOn;
var doctorID;
var clinicID;
var triageCategory;
var gCScore;
var lineItemNo;
DateTime? vitalSignDate;
var actualTimeTaken;
var sugarLevel;
var fBS;
var rBS;
var observationType;
var heartRate;
var muscleTone;
var reflexIrritability;
var bodyColor;
var isFirstAssessment;
var dateofBirth;
var timeOfBirth;
var bloodPressure;
var bloodPressureCuffLocationDesc;
var bloodPressureCuffSizeDesc;
var bloodPressurePatientPositionDesc;
var clinicName;
var doctorImageURL;
var doctorName;
var painScoreDesc;
var pulseRhythmDesc;
var respirationPatternDesc;
var temperatureCelciusMethodDesc;
var time;
VitalSignResModel(
{this.transNo,
this.projectID,
this.weightKg,
this.heightCm,
this.temperatureCelcius,
this.pulseBeatPerMinute,
this.respirationBeatPerMinute,
this.bloodPressureLower,
this.bloodPressureHigher,
this.sAO2,
this.fIO2,
this.painScore,
this.bodyMassIndex,
this.headCircumCm,
this.leanBodyWeightLbs,
this.idealBodyWeightLbs,
this.temperatureCelciusMethod,
this.pulseRhythm,
this.respirationPattern,
this.bloodPressureCuffLocation,
this.bloodPressureCuffSize,
this.bloodPressurePatientPosition,
this.painLocation,
this.painDuration,
this.painCharacter,
this.painFrequency,
this.isPainManagementDone,
this.status,
this.isVitalsRequired,
this.patientID,
this.createdOn,
this.doctorID,
this.clinicID,
this.triageCategory,
this.gCScore,
this.lineItemNo,
this.vitalSignDate,
this.actualTimeTaken,
this.sugarLevel,
this.fBS,
this.rBS,
this.observationType,
this.heartRate,
this.muscleTone,
this.reflexIrritability,
this.bodyColor,
this.isFirstAssessment,
this.dateofBirth,
this.timeOfBirth,
this.bloodPressure,
this.bloodPressureCuffLocationDesc,
this.bloodPressureCuffSizeDesc,
this.bloodPressurePatientPositionDesc,
this.clinicName,
this.doctorImageURL,
this.doctorName,
this.painScoreDesc,
this.pulseRhythmDesc,
this.respirationPatternDesc,
this.temperatureCelciusMethodDesc,
this.time});
VitalSignResModel.fromJson(Map<String, dynamic> json) {
transNo = json['TransNo'];
projectID = json['ProjectID'];
weightKg = json['WeightKg'];
heightCm = json['HeightCm'];
temperatureCelcius = json['TemperatureCelcius'];
pulseBeatPerMinute = json['PulseBeatPerMinute'];
respirationBeatPerMinute = json['RespirationBeatPerMinute'];
bloodPressureLower = json['BloodPressureLower'];
bloodPressureHigher = json['BloodPressureHigher'];
sAO2 = json['SAO2'];
fIO2 = json['FIO2'];
painScore = json['PainScore'];
bodyMassIndex = json['BodyMassIndex'];
headCircumCm = json['HeadCircumCm'];
leanBodyWeightLbs = json['LeanBodyWeightLbs'];
idealBodyWeightLbs = json['IdealBodyWeightLbs'];
temperatureCelciusMethod = json['TemperatureCelciusMethod'];
pulseRhythm = json['PulseRhythm'];
respirationPattern = json['RespirationPattern'];
bloodPressureCuffLocation = json['BloodPressureCuffLocation'];
bloodPressureCuffSize = json['BloodPressureCuffSize'];
bloodPressurePatientPosition = json['BloodPressurePatientPosition'];
painLocation = json['PainLocation'];
painDuration = json['PainDuration'];
painCharacter = json['PainCharacter'];
painFrequency = json['PainFrequency'];
isPainManagementDone = json['IsPainManagementDone'];
status = json['Status'];
isVitalsRequired = json['IsVitalsRequired'];
patientID = json['PatientID'];
createdOn = json['CreatedOn'];
doctorID = json['DoctorID'];
clinicID = json['ClinicID'];
triageCategory = json['TriageCategory'];
gCScore = json['GCScore'];
lineItemNo = json['LineItemNo'];
vitalSignDate = DateUtil.convertStringToDate(json['CreatedOn']);
actualTimeTaken = json['ActualTimeTaken'];
sugarLevel = json['SugarLevel'];
fBS = json['FBS'];
rBS = json['RBS'];
observationType = json['ObservationType'];
heartRate = json['HeartRate'];
muscleTone = json['MuscleTone'];
reflexIrritability = json['ReflexIrritability'];
bodyColor = json['BodyColor'];
isFirstAssessment = json['IsFirstAssessment'];
dateofBirth = json['DateofBirth'];
timeOfBirth = json['TimeOfBirth'];
bloodPressure = json['BloodPressure'];
bloodPressureCuffLocationDesc = json['BloodPressureCuffLocationDesc'];
bloodPressureCuffSizeDesc = json['BloodPressureCuffSizeDesc'];
bloodPressurePatientPositionDesc = json['BloodPressurePatientPositionDesc'];
clinicName = json['ClinicName'];
doctorImageURL = json['DoctorImageURL'];
doctorName = json['DoctorName'];
painScoreDesc = json['PainScoreDesc'];
pulseRhythmDesc = json['PulseRhythmDesc'];
respirationPatternDesc = json['RespirationPatternDesc'];
temperatureCelciusMethodDesc = json['TemperatureCelciusMethodDesc'];
time = json['Time'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['TransNo'] = this.transNo;
data['ProjectID'] = this.projectID;
data['WeightKg'] = this.weightKg;
data['HeightCm'] = this.heightCm;
data['TemperatureCelcius'] = this.temperatureCelcius;
data['PulseBeatPerMinute'] = this.pulseBeatPerMinute;
data['RespirationBeatPerMinute'] = this.respirationBeatPerMinute;
data['BloodPressureLower'] = this.bloodPressureLower;
data['BloodPressureHigher'] = this.bloodPressureHigher;
data['SAO2'] = this.sAO2;
data['FIO2'] = this.fIO2;
data['PainScore'] = this.painScore;
data['BodyMassIndex'] = this.bodyMassIndex;
data['HeadCircumCm'] = this.headCircumCm;
data['LeanBodyWeightLbs'] = this.leanBodyWeightLbs;
data['IdealBodyWeightLbs'] = this.idealBodyWeightLbs;
data['TemperatureCelciusMethod'] = this.temperatureCelciusMethod;
data['PulseRhythm'] = this.pulseRhythm;
data['RespirationPattern'] = this.respirationPattern;
data['BloodPressureCuffLocation'] = this.bloodPressureCuffLocation;
data['BloodPressureCuffSize'] = this.bloodPressureCuffSize;
data['BloodPressurePatientPosition'] = this.bloodPressurePatientPosition;
data['PainLocation'] = this.painLocation;
data['PainDuration'] = this.painDuration;
data['PainCharacter'] = this.painCharacter;
data['PainFrequency'] = this.painFrequency;
data['IsPainManagementDone'] = this.isPainManagementDone;
data['Status'] = this.status;
data['IsVitalsRequired'] = this.isVitalsRequired;
data['PatientID'] = this.patientID;
data['CreatedOn'] = this.createdOn;
data['DoctorID'] = this.doctorID;
data['ClinicID'] = this.clinicID;
data['TriageCategory'] = this.triageCategory;
data['GCScore'] = this.gCScore;
data['LineItemNo'] = this.lineItemNo;
data['VitalSignDate'] = this.vitalSignDate;
data['ActualTimeTaken'] = this.actualTimeTaken;
data['SugarLevel'] = this.sugarLevel;
data['FBS'] = this.fBS;
data['RBS'] = this.rBS;
data['ObservationType'] = this.observationType;
data['HeartRate'] = this.heartRate;
data['MuscleTone'] = this.muscleTone;
data['ReflexIrritability'] = this.reflexIrritability;
data['BodyColor'] = this.bodyColor;
data['IsFirstAssessment'] = this.isFirstAssessment;
data['DateofBirth'] = this.dateofBirth;
data['TimeOfBirth'] = this.timeOfBirth;
data['BloodPressure'] = this.bloodPressure;
data['BloodPressureCuffLocationDesc'] = this.bloodPressureCuffLocationDesc;
data['BloodPressureCuffSizeDesc'] = this.bloodPressureCuffSizeDesc;
data['BloodPressurePatientPositionDesc'] =
this.bloodPressurePatientPositionDesc;
data['ClinicName'] = this.clinicName;
data['DoctorImageURL'] = this.doctorImageURL;
data['DoctorName'] = this.doctorName;
data['PainScoreDesc'] = this.painScoreDesc;
data['PulseRhythmDesc'] = this.pulseRhythmDesc;
data['RespirationPatternDesc'] = this.respirationPatternDesc;
data['TemperatureCelciusMethodDesc'] = this.temperatureCelciusMethodDesc;
data['Time'] = this.time;
return data;
}
}

@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart' show CapExtension;
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';

@ -7,10 +7,13 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/covid_questionnare_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/presentation/covid19test/covid_review_screen.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/CustomSwitch.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
@ -46,87 +49,104 @@ class _Covid19QuestionnaireState extends State<Covid19Questionnaire> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(children: [
Expanded(
child: CollapsingListView(
return CollapsingListView(
title: "COVID-19",
child: Padding(
bottomChild: Container(
padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
hasShadow: true,
customBorder: BorderRadius.only(
topLeft: Radius.circular(24.r),
topRight: Radius.circular(24.r),
),
),child: CustomButton(
text: "Next".needTranslation,
onPressed: () {
moveToNextPage(context);
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
height: 56.h,
)),
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(24.w),
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(20.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Please answer below questionnaire:".toText14(
color: AppColors.textColor,
weight: FontWeight.w500,
),
SizedBox(height: 20.h),
// Question list
ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: qaList.length,
separatorBuilder: (context, index) => SizedBox(height: 16.h),
itemBuilder: (context, index) {
final question = qaList[index];
final isAnswerYes = question.ans == 1;
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(20.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Please answer below questionnaire:".toText14(
color: AppColors.textColor,
weight: FontWeight.w500,
),
SizedBox(height: 20.h),
// Question list
ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: qaList.length,
separatorBuilder: (context, index) => SizedBox(height: 16.h),
itemBuilder: (context, index) {
final question = qaList[index];
final isAnswerYes = question.ans == 1;
return Row(
children: [
Expanded(
child: (question.questionEn ?? '').toText14(
color: AppColors.textColor,
weight: FontWeight.w400,
),
),
SizedBox(width: 12.w),
CustomSwitch(
value: isAnswerYes,
onChanged: (value) => _toggleAnswer(index, value),
),
],
);
},
),
],
return Row(
children: [
Expanded(
child: (question.questionEn ?? '').toText14(
color: AppColors.textColor,
weight: FontWeight.w400,
),
),
SizedBox(width: 12.w),
CustomSwitch(
value: isAnswerYes,
onChanged: (value) => _toggleAnswer(index, value),
),
],
);
},
),
),
],
),
),
),
SizedBox(height: 16.h),
// Next button
CustomButton(
text: "Next".needTranslation,
onPressed: () {
// Handle next action
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
height: 56.h,
),
],
),
),
),
),
),
]));
);
}
moveToNextPage(BuildContext context) async{
LoaderBottomSheet.showLoader();
await hmgServicesViewModel.getCovidProcedureList();
await hmgServicesViewModel.getPaymentInfo(procedureID: hmgServicesViewModel.covidTestProcedureList[0].procedureId!);
LoaderBottomSheet.hideLoader();
Navigator.of(context)
.push(
CustomPageRoute(
page: CovidReviewScreen(selectedHospital: widget.selectedHospital, qaList: qaList),
),
);
}
}

@ -0,0 +1,565 @@
import 'dart:async';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import 'package:smooth_corner/smooth_corner.dart';
// Added imports required by this file
import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
/// A reusable payment screen for COVID-related payments.
///
/// This screen re-uses the same UI pattern and payment flow used by
/// `AppointmentPaymentPage` (in-app browser, Apple Pay and payfort status checks),
/// but it keeps the post-payment handling generic (shows success / failure)
/// so it can be safely used for COVID test purchases without appointment-specific APIs.
class CovidPaymentScreen extends StatefulWidget {
final double amount;
final int projectID;
final int clinicID;
final String procedureId;
final double taxAmount;
final String title;
const CovidPaymentScreen({
super.key,
required this.amount,
required this.projectID,
required this.clinicID,
required this.procedureId,
this.taxAmount = 0.0,
this.title = "COVID Payment",
});
@override
State<CovidPaymentScreen> createState() => _CovidPaymentScreenState();
}
class _CovidPaymentScreenState extends State<CovidPaymentScreen> {
late PayfortViewModel payfortViewModel;
late AppState appState;
MyInAppBrowser? browser;
String selectedPaymentMethod = "";
String transID = "";
bool isShowTamara = false; // placeholder: could be enabled based on remote config
@override
void initState() {
super.initState();
// initialize payfort view model when the widget is ready
scheduleMicrotask(() {
payfortViewModel = Provider.of<PayfortViewModel>(context, listen: false);
payfortViewModel.initPayfortViewModel();
payfortViewModel.setIsApplePayConfigurationLoading(false);
// Optionally compute if Tamara should be shown by calling a remote config API.
// For now keep it false (unless the app provides an API for it).
// Enable Tamara payment option for COVID screen
setState(() {
isShowTamara = true;
});
});
}
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
payfortViewModel = Provider.of<PayfortViewModel>(context);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: widget.title.needTranslation,
bottomChild: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
"Total amount to pay".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 17.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Amount before tax".needTranslation.toText14(isBold: true),
Utils.getPaymentAmountWithSymbol(( (widget.amount - widget.taxAmount).toString()).toText16(isBold: true), AppColors.blackColor, 13, isSaudiCurrency: true),
],
).paddingSymmetrical(24.h, 0.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor),
// Show VAT amount passed from review screen
Utils.getPaymentAmountWithSymbol((widget.taxAmount.toString()).toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13, isSaudiCurrency: true),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 17.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"".needTranslation.toText14(isBold: true),
Utils.getPaymentAmountWithSymbol(widget.amount.toString().toText24(isBold: true), AppColors.blackColor, 17, isSaudiCurrency: true),
],
).paddingSymmetrical(24.h, 0.h),
// Apple Pay (iOS)
Platform.isIOS
? Utils.buildSvgWithAssets(
icon: AppAssets.apple_pay_button,
width: 200.h,
height: 80.h,
fit: BoxFit.contain,
).paddingSymmetrical(24.h, 0.h).onPress(() {
if (Utils.havePrivilege(103)) {
startApplePay();
} else {
openPaymentURL("ApplePay");
}
})
: SizedBox(height: 12.h),
SizedBox(height: 12.h),
// Action buttons: Cancel + Next (Next opens default payment flow - e.g. Visa)
// Padding(
// padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 24.h),
// child: Row(
// children: [
// Expanded(
// child: CustomButton(
// height: 56.h,
// text: LocaleKeys.cancel.tr(),
// onPressed: () {
// Navigator.of(context).pop();
// },
// backgroundColor: AppColors.secondaryLightRedColor,
// borderColor: AppColors.secondaryLightRedColor,
// textColor: AppColors.primaryRedColor,
// icon: AppAssets.cancel,
// iconColor: AppColors.primaryRedColor,
// borderRadius: 12.r,
// ),
// ),
// SizedBox(width: 8.h),
// Expanded(
// child: CustomButton(
// height: 56.h,
// text: "Next".needTranslation,
// onPressed: () {
// // Default to Visa for Next
// selectedPaymentMethod = "VISA";
// openPaymentURL("visa");
// },
// backgroundColor: AppColors.primaryRedColor,
// borderColor: AppColors.primaryRedColor,
// textColor: AppColors.whiteColor,
// fontSize: 16.f,
// fontWeight: FontWeight.w500,
// borderRadius: 12.r,
// padding: EdgeInsets.symmetric(horizontal: 10.w),
// icon: AppAssets.add_icon,
// iconColor: AppColors.whiteColor,
// iconSize: 18.h,
// ),
// ),
// ],
// ),
// ),
],
),
).paddingSymmetrical(0.h, 0.h),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
// MADA tile
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.mada, width: 72.h, height: 25.h),
SizedBox(height: 16.h),
"Mada".needTranslation.toText16(isBold: true),
],
),
SizedBox(width: 8.h),
const Spacer(),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
),
],
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "MADA";
openPaymentURL("MADA");
}),
SizedBox(height: 16.h),
// Visa / Mastercard tile
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Image.asset(AppAssets.visa, width: 50.h, height: 50.h),
SizedBox(width: 8.h),
Image.asset(AppAssets.mastercard, width: 40.h, height: 40.h),
],
),
SizedBox(height: 16.h),
"Visa or Mastercard".needTranslation.toText16(isBold: true),
],
),
SizedBox(width: 8.h),
const Spacer(),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
),
],
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "VISA";
openPaymentURL("VISA");
}),
SizedBox(height: 16.h),
// Optional Tamara tile (shown only if enabled)
isShowTamara
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(
AppAssets.tamaraEng,
width: 72.h,
height: 25.h,
fit: BoxFit.contain,
// If PNG fails to load for any reason, log and fallback to SVG asset
errorBuilder: (context, error, stackTrace) {
debugPrint('Failed to load Tamara PNG asset: $error');
return Utils.buildSvgWithAssets(
icon: AppAssets.tamara,
width: 72.h,
height: 25.h,
fit: BoxFit.contain,
);
},
),
SizedBox(height: 16.h),
"Tamara".needTranslation.toText16(isBold: true),
],
),
SizedBox(width: 8.h),
const Spacer(),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
),
],
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "TAMARA";
openPaymentURL("TAMARA");
})
: SizedBox.shrink(),
SizedBox(height: 24.h),
// Bottom payment summary
SizedBox(height: 24.h),
],
),
),
),
);
}
void onBrowserLoadStart(String url) {
// Generic loader hook: detect success / error patterns from the in-app browser.
// Keep parsing defensive: use Uri.parse where possible.
try {
final uri = Uri.tryParse(url);
if (selectedPaymentMethod == "TAMARA" && uri != null) {
// tamara returns different query param names depending on platform; defensive checks
final params = uri.queryParameters;
if (params.isNotEmpty) {
// example keys: 'status', 'AuthorizePaymentId' (android) or 'paymentStatus', 'orderId' (iOS)
}
}
} catch (e) {
debugPrint('onBrowserLoadStart parse error: $e');
}
MyInAppBrowser.successURLS.forEach((element) {
if (url.contains(element)) {
browser?.close();
MyInAppBrowser.isPaymentDone = true;
return;
}
});
MyInAppBrowser.errorURLS.forEach((element) {
if (url.contains(element)) {
browser?.close();
MyInAppBrowser.isPaymentDone = false;
return;
}
});
}
void onBrowserExit(bool isPaymentMade) async {
// When browser closes, check payment status using payfort view model
await checkPaymentStatus();
}
Future<void> checkPaymentStatus() async {
LoaderBottomSheet.showLoader(loadingText: "Checking payment status, Please wait...".needTranslation);
try {
await payfortViewModel.checkPaymentStatus(transactionID: transID, onSuccess: (apiResponse) async {
// treat any successful responseMessage as success; otherwise show generic error
final success = payfortViewModel.payfortCheckPaymentStatusResponseModel?.responseMessage?.toLowerCase() == 'success';
LoaderBottomSheet.hideLoader();
if (success) {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getSuccessWidget(loadingText: "Payment successful".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
} else {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
});
} catch (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err.toString()),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}
void openPaymentURL(String paymentMethod) {
browser = MyInAppBrowser(onExitCallback: onBrowserExit, onLoadStartCallback: onBrowserLoadStart, context: context);
transID = Utils.getAppointmentTransID(widget.projectID, widget.clinicID, DateTime.now().millisecondsSinceEpoch);
// Open payment browser with essential parameters; many fields are simplified here
browser?.openPaymentBrowser(
widget.amount,
"COVID Test Payment",
transID,
widget.projectID.toString(),
"CustID_${appState.getAuthenticatedUser()?.patientId ?? ''}@HMG.com",
selectedPaymentMethod,
appState.getAuthenticatedUser()?.patientType.toString() ?? "",
appState.getAuthenticatedUser() != null ? "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}" : "",
appState.getAuthenticatedUser()?.patientId.toString() ?? "",
appState.getAuthenticatedUser() ?? (null as dynamic),
browser!,
false,
"2",
"",
context,
DateTime.now().toString(),
"",
0,
0,
"3",
);
}
void startApplePay() async {
showCommonBottomSheet(
context,
child: Utils.getLoadingWidget(),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false,
);
transID = Utils.getAppointmentTransID(widget.projectID, widget.clinicID, DateTime.now().millisecondsSinceEpoch);
// Prepare a minimal apple pay request using payfortViewModel's configuration
try {
await payfortViewModel.getPayfortConfigurations(serviceId: 0, projectId: widget.projectID, integrationId: 2);
// Build minimal apple pay request (model omitted here to keep things generic)
ApplePayInsertRequest applePayInsertRequest = ApplePayInsertRequest(
clientRequestID: transID,
clinicID: widget.clinicID,
currency: appState.getAuthenticatedUser() != null && appState.getAuthenticatedUser()!.outSa == 0 ? "SAR" : "AED",
customerEmail: "CustID_${appState.getAuthenticatedUser()?.patientId ?? ''}@HMG.com",
customerID: appState.getAuthenticatedUser()?.patientId,
customerName: appState.getAuthenticatedUser() != null ? "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}" : "",
deviceToken: await Utils.getStringFromPrefs(CacheConst.pushToken),
voipToken: await Utils.getStringFromPrefs(CacheConst.voipToken),
doctorID: 0,
projectID: widget.projectID.toString(),
serviceID: "0",
channelID: 3,
patientID: appState.getAuthenticatedUser()?.patientId,
patientTypeID: appState.getAuthenticatedUser()?.patientType,
patientOutSA: appState.getAuthenticatedUser()?.outSa,
appointmentDate: DateTime.now().toString(),
appointmentNo: 0,
orderDescription: "COVID Test Payment",
liveServiceID: "0",
latitude: "0.0",
longitude: "0.0",
amount: widget.amount.toString(),
isSchedule: "0",
language: appState.isArabic() ? 'ar' : 'en',
languageID: appState.isArabic() ? 1 : 2,
userName: appState.getAuthenticatedUser()?.patientId,
responseContinueURL: "http://hmg.com/Documents/success.html",
backClickUrl: "http://hmg.com/Documents/success.html",
paymentOption: "ApplePay",
isMobSDK: true,
merchantReference: transID,
merchantIdentifier: payfortViewModel.payfortProjectDetailsRespModel?.merchantIdentifier,
commandType: "PURCHASE",
signature: payfortViewModel.payfortProjectDetailsRespModel?.signature,
accessCode: payfortViewModel.payfortProjectDetailsRespModel?.accessCode,
shaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel?.shaRequest,
shaResponsePhrase: payfortViewModel.payfortProjectDetailsRespModel?.shaResponse,
returnURL: "",
);
await payfortViewModel.applePayRequestInsert(applePayInsertRequest: applePayInsertRequest).then((value) {
// Start apple pay flow
payfortViewModel.paymentWithApplePay(
customerName: appState.getAuthenticatedUser() != null ? "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}" : "",
customerEmail: "CustID_${appState.getAuthenticatedUser()?.patientId ?? ''}@HMG.com",
orderDescription: "COVID Test Payment",
orderAmount: widget.amount,
merchantReference: transID,
merchantIdentifier: payfortViewModel.payfortProjectDetailsRespModel?.merchantIdentifier ?? "",
applePayAccessCode: payfortViewModel.payfortProjectDetailsRespModel?.accessCode ?? "",
applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel?.shaRequest ?? "",
currency: appState.getAuthenticatedUser() != null && appState.getAuthenticatedUser()!.outSa == 0 ? "SAR" : "AED",
onFailed: (failureResult) async {
Navigator.of(context).pop();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: failureResult.message.toString()),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
onSucceeded: (successResult) async {
Navigator.of(context).pop();
selectedPaymentMethod = successResult.paymentOption ?? "VISA";
await checkPaymentStatus();
},
);
}).catchError((e) {
Navigator.of(context).pop();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: e.toString()),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
} catch (e) {
Navigator.of(context).pop();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: e.toString()),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}
}

@ -0,0 +1,441 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/covid_get_test_proceedure_resp.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/covid_questionnare_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/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:provider/provider.dart';
import 'package:hmg_patient_app_new/presentation/covid19test/covid_payment_screen.dart';
class CovidReviewScreen extends StatefulWidget {
final HospitalsModel selectedHospital;
final List<CovidQuestionnaireModel> qaList;
const CovidReviewScreen({super.key, required this.selectedHospital, required this.qaList});
@override
State<CovidReviewScreen> createState() => _CovidReviewScreenState();
}
class _CovidReviewScreenState extends State<CovidReviewScreen> {
late HmgServicesViewModel hmgServicesViewModel;
bool _acceptedTerms =false;
Covid19GetTestProceduresResp? _selectedProcedure;
@override
void initState() {
super.initState();
hmgServicesViewModel = Provider.of<HmgServicesViewModel>(context, listen: false);
scheduleMicrotask(() {
});
}
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: "COVID-19",
bottomChild: Consumer<HmgServicesViewModel>(builder: (context, vm, _) {
final info = vm.covidPaymentInfo;
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
),
child: SizedBox(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (info == null) ...[
// show a placeholder/loading while payment info is null
SizedBox(height: 24.h),
Center(child: CircularProgressIndicator()),
SizedBox(height: 24.h),
] else ...[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Amount before tax".needTranslation.toText18(isBold: true),
Utils.getPaymentAmountWithSymbol(
(info.patientShareField ?? 0).toString().toText16(isBold: true),
AppColors.blackColor,
13,
isSaudiCurrency: true),
],
),
SizedBox(height: 4.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Tax Amount".needTranslation.toText14(isBold: true),
Utils.getPaymentAmountWithSymbol(
(info.patientTaxAmountField ?? 0).toString().toText16(isBold: true),
AppColors.blackColor,
13,
isSaudiCurrency: true)
],
),
SizedBox(height: 18.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: 150.h,
child: Utils.getPaymentMethods(),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Utils.getPaymentAmountWithSymbol(
(info.patientShareWithTaxField ?? 0).toString().toText24(isBold: true),
AppColors.blackColor,
17,
isSaudiCurrency: true),
],
),
],
)
],
).paddingOnly(left: 16.h, top: 24.h, right: 16.h, bottom: 0.h),
GestureDetector(
onTap: () {
setState(() {
_acceptedTerms = !_acceptedTerms;
});
},
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 12.r,
hasShadow: false,
),
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h),
child: Row(
children: [
Container(
width: 20.w,
height: 20.h,
decoration: BoxDecoration(
color: _acceptedTerms
? AppColors.primaryRedColor
: AppColors.whiteColor,
border: Border.all(
color: _acceptedTerms
? AppColors.primaryRedColor
: AppColors.greyTextColor.withValues(alpha: 0.3),
width: 2,
),
borderRadius: BorderRadius.circular(4.r),
),
child: _acceptedTerms
? Icon(
Icons.check,
size: 14.h,
color: AppColors.whiteColor,
)
: null,
),
SizedBox(width: 12.w),
Expanded(
child: RichText(
text: TextSpan(
style: TextStyle(
fontSize: 14.f,
color: AppColors.textColor,
fontWeight: FontWeight.w400,
),
children: [
const TextSpan(text: "I agree to the "),
TextSpan(
text: "terms and conditions",
style: TextStyle(
color: AppColors.primaryRedColor,
fontWeight: FontWeight.w600,
decoration: TextDecoration.underline,
decorationColor: AppColors.primaryRedColor,
),
),
],
),
),
),
],
),
),
),
// Two-button layout: Cancel (left) and Next (right)
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 24.h),
child: Row(
children: [
Expanded(
child: CustomButton(
height: 56.h,
text: LocaleKeys.cancel.tr(),
onPressed: () {
Navigator.of(context).pop();
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
// icon: AppAssets.cancel,
// iconColor: AppColors.primaryRedColor,
borderRadius: 12.r,
),
),
SizedBox(width: 8.h),
Expanded(
child: CustomButton(
height: 56.h,
text: "Next".needTranslation,
onPressed: () async {
// Validate selection and payment info
if (_selectedProcedure == null) {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Please select a procedure".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
return;
}
if (info == null) {
// If payment info missing, attempt to fetch
LoaderBottomSheet.showLoader();
try {
await hmgServicesViewModel.getPaymentInfo(procedureID: _selectedProcedure!.procedureId!);
} catch (e) {
debugPrint('getPaymentInfo error: $e');
} finally {
LoaderBottomSheet.hideLoader();
}
if (hmgServicesViewModel.covidPaymentInfo == null) {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Payment information not available".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
return;
}
}
// Compute amount and project/clinic IDs defensively
final paymentInfo = hmgServicesViewModel.covidPaymentInfo ?? info!;
final double amount = paymentInfo.patientShareWithTaxField ?? (paymentInfo.patientShareField?.toDouble() ?? 0.0);
// projectID may be int or string; parse defensively
int projectID = 0;
try {
final p = widget.selectedHospital.mainProjectID;
if (p is int) projectID = p;
else if (p is String) projectID = int.tryParse(p) ?? 0;
} catch (_) {}
int clinicID = 0;
try {
clinicID = int.tryParse(widget.selectedHospital.setupID ?? '') ?? int.tryParse(widget.selectedHospital.iD?.toString() ?? '') ?? 0;
} catch (_) {}
// Navigate to payment screen
Navigator.of(context).push(
CustomPageRoute(
page: CovidPaymentScreen(
amount: amount,
projectID: projectID,
clinicID: clinicID,
procedureId: _selectedProcedure!.procedureId ?? '',
taxAmount: paymentInfo.patientTaxAmountField?.toDouble() ?? 0.0,
),
),
);
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
// icon: AppAssets.add_icon,
// iconColor: AppColors.whiteColor,
iconSize: 18.h,
),
),
],
),
)
]
],
),
),
);
}),
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Please select the procedure:".toText14(
color: AppColors.textColor,
weight: FontWeight.w500,
),
SizedBox(height: 16.h),
Consumer<HmgServicesViewModel>(
builder: (context, vm, _) {
final procedures = vm.covidTestProcedureList;
// ensure default selection is first item (once available)
if (_selectedProcedure == null && procedures.isNotEmpty) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
setState(() {
_selectedProcedure = procedures[0];
});
// also fetch payment info for default selection and show loader while loading
if (procedures[0].procedureId != null) {
LoaderBottomSheet.showLoader();
hmgServicesViewModel
.getPaymentInfo(procedureID: procedures[0].procedureId!)
.whenComplete(() {
LoaderBottomSheet.hideLoader();
});
}
}
});
}
// Use a shrink-wrapped ListView.separated and match prescription styling
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16.h),
child: ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: procedures.length,
separatorBuilder: (context, index) => Divider(
height: 1.h,
thickness: 1.h,
color: AppColors.borderOnlyColor.withValues(alpha: 0.05),
).paddingOnly(top:8,bottom:16.h),
itemBuilder: (context, index) {
final item = procedures[index];
// Let the radio option widget manage its own padding to avoid doubled spacing
return _buildRadioOption(value: item, title: item.procedureName ?? '');
},
).paddingOnly(bottom:16.h)
),
);
},
),
],
),
),
),
);
}
Widget _buildRadioOption({
required Covid19GetTestProceduresResp value,
required String title,
}) {
final bool isSelected = _selectedProcedure?.procedureId == value.procedureId;
return GestureDetector(
onTap: () async {
setState(() {
_selectedProcedure = value;
});
// show bottomsheet loader while fetching payment info
if (value.procedureId != null) {
LoaderBottomSheet.showLoader();
try {
await hmgServicesViewModel.getPaymentInfo(procedureID: value.procedureId!, projectID: widget.selectedHospital.mainProjectID);
} catch (e) {
debugPrint('getPaymentInfo error: $e');
} finally {
LoaderBottomSheet.hideLoader();
}
}
},
child: Row(
children: [
Container(
width: 20.h,
height: 20.h,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: isSelected
? AppColors.primaryRedColor
: AppColors.greyTextColor.withValues(alpha: 0.3),
width: 2,
),
),
child: isSelected
? Center(
child: Container(
width: 10.h,
height: 10.h,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColors.primaryRedColor,
),
),
)
: null,
),
SizedBox(width: 12.h),
Expanded(
child: title.toText14(
color: AppColors.textColor,
weight: FontWeight.w400,
),
),
],
)
// Keep only bottom padding here and rely on the surrounding container's left/right inset
.paddingOnly(left: 0.h, right: 0.h, top: 0.h, bottom: 12.h),
);
}
}

@ -96,23 +96,32 @@ class ServicesPage extends StatelessWidget {
page: BloodDonationPage(),
),
);
// }, onError: (err) {
// LoaderBottomSheet.hideLoader();
// });
}, onError: (err) {
LoaderBottomSheet.hideLoader();
});
},
),
HmgServicesComponentModel(
11,
"Covid 19 Test".needTranslation,
"".needTranslation,
AppAssets.covid19icon,
bgColor: AppColors.covid29Color,
true,
route: AppRoutes.covid19Test,
)
// }, onError: (err) {
// LoaderBottomSheet.hideLoader();
// });
}, onError: (err) {
LoaderBottomSheet.hideLoader();
});
}),
// HmgServicesComponentModel(
// 11,
// "Covid 19 Test".needTranslation,
// "".needTranslation,
// AppAssets.covid19icon,
// bgColor: AppColors.covid29Color,
// true,
// route: AppRoutes.covid19Test,
// ),
// HmgServicesComponentModel(
// 11,
// "Vital Sign".needTranslation,
// "".needTranslation,
// AppAssets.covid19icon,
// bgColor: AppColors.covid29Color,
// true,
// route: AppRoutes.vitalSign,
// )
// HmgServicesComponentModel(
// 3,

@ -0,0 +1,308 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/vital_sign_respo_model.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/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:provider/provider.dart';
class VitalSignPage extends StatefulWidget {
const VitalSignPage({super.key});
@override
State<VitalSignPage> createState() => _VitalSignPageState();
}
class _VitalSignPageState extends State<VitalSignPage> {
@override
void initState() {
super.initState();
final HmgServicesViewModel hmgServicesViewModel = context.read<HmgServicesViewModel>();
scheduleMicrotask(() async {
LoaderBottomSheet.showLoader(loadingText: 'Loading Vital Signs...');
await hmgServicesViewModel.getPatientVitalSign(
onSuccess: (_) {
LoaderBottomSheet.hideLoader();
},
onError: (_) {
LoaderBottomSheet.hideLoader();
},
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: 'Vital Signs',
child: Consumer<HmgServicesViewModel>(
builder: (context, viewModel, child) {
// Get the latest vital sign data (first item in the list)
VitalSignResModel? latestVitalSign = viewModel.vitalSignList.isNotEmpty
? viewModel.vitalSignList.first
: null;
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
// Main content with body image
Padding(
padding: EdgeInsets.symmetric(horizontal: 24.h),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Left side - Vital Sign Cards
Expanded(
child: Column(
children: [
// BMI Card
_buildVitalSignCard(
icon: AppAssets.activity,
iconColor: AppColors.successColor,
iconBgColor: AppColors.successColor.withValues(alpha: 0.1),
label: 'BMI',
value: latestVitalSign?.bodyMassIndex?.toString() ?? '--',
unit: '',
chipText: _getBMIStatus(latestVitalSign?.bodyMassIndex),
chipBgColor: AppColors.successColor.withValues(alpha: 0.1),
chipTextColor: AppColors.successColor,
),
SizedBox(height: 16.h),
// Height Card
_buildVitalSignCard(
icon: AppAssets.height,
iconColor: AppColors.infoColor,
iconBgColor: AppColors.infoColor.withValues(alpha: 0.1),
label: 'Height',
value: latestVitalSign?.heightCm?.toString() ?? '--',
unit: 'cm',
),
SizedBox(height: 16.h),
// Weight Card
_buildVitalSignCard(
icon: AppAssets.weight,
iconColor: AppColors.successColor,
iconBgColor: AppColors.successColor.withValues(alpha: 0.1),
label: 'Weight',
value: latestVitalSign?.weightKg?.toString() ?? '--',
unit: 'kg',
chipText: 'Normal',
chipBgColor: AppColors.successColor.withValues(alpha: 0.1),
chipTextColor: AppColors.successColor,
),
SizedBox(height: 16.h),
// Blood Pressure Card
_buildVitalSignCard(
icon: AppAssets.activity,
iconColor: AppColors.warningColor,
iconBgColor: AppColors.warningColor.withValues(alpha: 0.1),
label: 'Blood Pressure',
value: latestVitalSign != null &&
latestVitalSign.bloodPressureHigher != null &&
latestVitalSign.bloodPressureLower != null
? '${latestVitalSign.bloodPressureHigher}/${latestVitalSign.bloodPressureLower}'
: '--',
unit: '',
),
SizedBox(height: 16.h),
// Temperature Card
_buildVitalSignCard(
icon: AppAssets.activity,
iconColor: AppColors.errorColor,
iconBgColor: AppColors.errorColor.withValues(alpha: 0.1),
label: 'Temperature',
value: latestVitalSign?.temperatureCelcius?.toString() ?? '--',
unit: '°C',
),
],
),
),
SizedBox(width: 16.h),
// Right side - Body Image and Heart Rate + Respiratory Rate
Expanded(
child: Column(
children: [
// Body anatomy image
Container(
height: 280.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Center(
child: Image.asset(
AppAssets.fullBodyFront,
height: 260.h,
fit: BoxFit.contain,
),
),
),
SizedBox(height: 16.h),
// Heart Rate Card
_buildVitalSignCard(
icon: AppAssets.heart,
iconColor: AppColors.errorColor,
iconBgColor: AppColors.errorColor.withValues(alpha: 0.1),
label: 'Heart Rate',
value: latestVitalSign?.heartRate?.toString() ??
latestVitalSign?.pulseBeatPerMinute?.toString() ?? '--',
unit: 'bpm',
chipText: 'Normal',
chipBgColor: AppColors.successColor.withValues(alpha: 0.1),
chipTextColor: AppColors.successColor,
),
SizedBox(height: 16.h),
// Respiratory rate Card
_buildVitalSignCard(
icon: AppAssets.activity,
iconColor: AppColors.successColor,
iconBgColor: AppColors.successColor.withValues(alpha: 0.1),
label: 'Respiratory rate',
value: latestVitalSign?.respirationBeatPerMinute?.toString() ?? '--',
unit: 'bpm',
chipText: 'Normal',
chipBgColor: AppColors.successColor.withValues(alpha: 0.1),
chipTextColor: AppColors.successColor,
),
],
),
),
],
),
),
SizedBox(height: 60.h),
],
),
);
},
),
),
);
}
String? _getBMIStatus(dynamic bmi) {
if (bmi == null) return null;
double bmiValue = double.tryParse(bmi.toString()) ?? 0;
if (bmiValue < 18.5) return 'Underweight';
if (bmiValue < 25) return 'Normal';
if (bmiValue < 30) return 'Overweight';
return 'Obese';
}
Widget _buildVitalSignCard({
required String icon,
required Color iconColor,
required Color iconBgColor,
required String label,
required String value,
required String unit,
String? chipText,
Color? chipBgColor,
Color? chipTextColor,
}) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(12.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
// Icon with background
Container(
padding: EdgeInsets.all(8.h),
decoration: BoxDecoration(
color: iconBgColor,
borderRadius: BorderRadius.circular(12.r),
),
child: Utils.buildSvgWithAssets(
icon: icon,
width: 16.w,
height: 16.h,
iconColor: iconColor,
fit: BoxFit.contain,
),
),
SizedBox(width: 8.w),
Expanded(
child: label.toText10(
color: AppColors.textColorLight,
weight: FontWeight.w500,
),
),
// Forward arrow
Utils.buildSvgWithAssets(
icon: AppAssets.arrow_forward,
width: 16.w,
height: 16.h,
iconColor: AppColors.textColorLight,
fit: BoxFit.contain,
),
],
),
SizedBox(height: 12.h),
// Value
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
value.toText18(
isBold: true,
color: AppColors.textColor,
),
if (unit.isNotEmpty) ...[
SizedBox(width: 4.w),
unit.toText12(
color: AppColors.textColorLight,
),
],
],
),
// Chip if available
if (chipText != null) ...[
SizedBox(height: 8.h),
AppCustomChipWidget(
labelText: chipText,
backgroundColor: chipBgColor,
textColor: chipTextColor,
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
),
],
],
),
),
);
}
}

@ -22,6 +22,7 @@ import 'package:hmg_patient_app_new/presentation/symptoms_checker/triage_screen.
import 'package:hmg_patient_app_new/presentation/symptoms_checker/user_info_selection.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/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_screen.dart';
import 'package:hmg_patient_app_new/presentation/water_monitor/water_monitor_settings_screen.dart';
import 'package:hmg_patient_app_new/splashPage.dart';
@ -45,6 +46,7 @@ class AppRoutes {
static const String smartWatches = '/smartWatches';
static const String huaweiHealthExample = '/huaweiHealthExample';
static const String covid19Test = '/covid19Test';
static const String vitalSign = '/vitalSign';
//appointments
static const String bookAppointmentPage = '/bookAppointmentPage';
@ -92,6 +94,7 @@ class AppRoutes {
waterConsumptionScreen: (context) => WaterConsumptionScreen(),
waterMonitorSettingsScreen: (context) => WaterMonitorSettingsScreen(),
healthCalculatorsPage: (context) => HealthCalculatorsPage(type: HealthCalConEnum.calculator),
healthConvertersPage: (context) => HealthCalculatorsPage(type: HealthCalConEnum.converter)
healthConvertersPage: (context) => HealthCalculatorsPage(type: HealthCalConEnum.converter),
vitalSign: (context) => VitalSignPage()
};
}

Loading…
Cancel
Save