e-referral work done search-referral in progress.
parent
9a04974db3
commit
d8a9d8443a
@ -0,0 +1,57 @@
|
||||
class CheckActivationCodeForEReferralRequestModel {
|
||||
String? logInTokenID;
|
||||
String? activationCode;
|
||||
double? versionID;
|
||||
int? channel;
|
||||
int? languageID;
|
||||
String? iPAdress;
|
||||
String? generalid;
|
||||
int? patientOutSA;
|
||||
dynamic sessionID;
|
||||
bool? isDentalAllowedBackend;
|
||||
int? deviceTypeID;
|
||||
|
||||
CheckActivationCodeForEReferralRequestModel(
|
||||
{this.logInTokenID,
|
||||
this.activationCode,
|
||||
this.versionID,
|
||||
this.channel,
|
||||
this.languageID,
|
||||
this.iPAdress,
|
||||
this.generalid,
|
||||
this.patientOutSA,
|
||||
this.sessionID,
|
||||
this.isDentalAllowedBackend,
|
||||
this.deviceTypeID});
|
||||
|
||||
CheckActivationCodeForEReferralRequestModel.fromJson(
|
||||
Map<String, dynamic> json) {
|
||||
logInTokenID = json['LogInTokenID'];
|
||||
activationCode = json['activationCode'];
|
||||
versionID = json['VersionID'];
|
||||
channel = json['Channel'];
|
||||
languageID = json['LanguageID'];
|
||||
iPAdress = json['IPAdress'];
|
||||
generalid = json['generalid'];
|
||||
patientOutSA = json['PatientOutSA'];
|
||||
sessionID = json['SessionID'];
|
||||
isDentalAllowedBackend = json['isDentalAllowedBackend'];
|
||||
deviceTypeID = json['DeviceTypeID'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['LogInTokenID'] = this.logInTokenID;
|
||||
data['activationCode'] = this.activationCode;
|
||||
data['VersionID'] = this.versionID;
|
||||
data['Channel'] = this.channel;
|
||||
data['LanguageID'] = this.languageID;
|
||||
data['IPAdress'] = this.iPAdress;
|
||||
data['generalid'] = this.generalid;
|
||||
data['PatientOutSA'] = this.patientOutSA;
|
||||
data['SessionID'] = this.sessionID;
|
||||
data['isDentalAllowedBackend'] = this.isDentalAllowedBackend;
|
||||
data['DeviceTypeID'] = this.deviceTypeID;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,155 @@
|
||||
class CreateEReferralRequestModel {
|
||||
bool? isInsuredPatient;
|
||||
String? cityCode;
|
||||
String? cityName;
|
||||
String? requesterName;
|
||||
String? requesterContactNo;
|
||||
int? requesterRelationship;
|
||||
String? otherRelationship;
|
||||
String? fullName;
|
||||
int? identificationNo;
|
||||
String? patientMobileNumber;
|
||||
int? preferredBranchCode;
|
||||
String? preferredBranchName;
|
||||
List<EReferralAttachment>? medicalReportAttachment;
|
||||
dynamic insuranceCardAttachment;
|
||||
double? versionID;
|
||||
int? channel;
|
||||
int? languageID;
|
||||
String? iPAdress;
|
||||
String? generalid;
|
||||
int? patientOutSA;
|
||||
String? sessionID;
|
||||
bool? isDentalAllowedBackend;
|
||||
int? deviceTypeID;
|
||||
int? patientID;
|
||||
String? tokenID;
|
||||
int? patientTypeID;
|
||||
int? patientType;
|
||||
|
||||
CreateEReferralRequestModel(
|
||||
{this.isInsuredPatient,
|
||||
this.cityCode,
|
||||
this.cityName,
|
||||
this.requesterName,
|
||||
this.requesterContactNo,
|
||||
this.requesterRelationship,
|
||||
this.otherRelationship,
|
||||
this.fullName,
|
||||
this.identificationNo,
|
||||
this.patientMobileNumber,
|
||||
this.preferredBranchCode,
|
||||
this.preferredBranchName,
|
||||
this.medicalReportAttachment,
|
||||
this.insuranceCardAttachment,
|
||||
this.versionID,
|
||||
this.channel,
|
||||
this.languageID,
|
||||
this.iPAdress,
|
||||
this.generalid,
|
||||
this.patientOutSA,
|
||||
this.sessionID,
|
||||
this.isDentalAllowedBackend,
|
||||
this.deviceTypeID,
|
||||
this.patientID,
|
||||
this.tokenID,
|
||||
this.patientTypeID,
|
||||
this.patientType});
|
||||
|
||||
CreateEReferralRequestModel.fromJson(Map<String, dynamic> json) {
|
||||
isInsuredPatient = json['IsInsuredPatient'];
|
||||
cityCode = json['CityCode'];
|
||||
cityName = json['CityName'];
|
||||
requesterName = json['RequesterName'];
|
||||
requesterContactNo = json['RequesterContactNo'];
|
||||
requesterRelationship = json['RequesterRelationship'];
|
||||
otherRelationship = json['OtherRelationship'];
|
||||
fullName = json['FullName'];
|
||||
identificationNo = json['IdentificationNo'];
|
||||
patientMobileNumber = json['PatientMobileNumber'];
|
||||
preferredBranchCode = json['PreferredBranchCode'];
|
||||
preferredBranchName = json['PreferredBranchName'];
|
||||
if (json['MedicalReportAttachment'] != null) {
|
||||
medicalReportAttachment = <EReferralAttachment>[];
|
||||
json['MedicalReportAttachment'].forEach((v) {
|
||||
medicalReportAttachment!.add(EReferralAttachment.fromJson(v));
|
||||
});
|
||||
}
|
||||
insuranceCardAttachment = json['InsuranceCardAttachment'] != null ? EReferralAttachment.fromJson(json['InsuranceCardAttachment']) : null;
|
||||
versionID = json['VersionID'];
|
||||
channel = json['Channel'];
|
||||
languageID = json['LanguageID'];
|
||||
iPAdress = json['IPAdress'];
|
||||
generalid = json['generalid'];
|
||||
patientOutSA = json['PatientOutSA'];
|
||||
sessionID = json['SessionID'];
|
||||
isDentalAllowedBackend = json['isDentalAllowedBackend'];
|
||||
deviceTypeID = json['DeviceTypeID'];
|
||||
patientID = json['PatientID'];
|
||||
tokenID = json['TokenID'];
|
||||
patientTypeID = json['PatientTypeID'];
|
||||
patientType = json['PatientType'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['IsInsuredPatient'] = this.isInsuredPatient;
|
||||
data['CityCode'] = this.cityCode;
|
||||
data['CityName'] = this.cityName;
|
||||
data['RequesterName'] = this.requesterName;
|
||||
data['RequesterContactNo'] = this.requesterContactNo;
|
||||
data['RequesterRelationship'] = this.requesterRelationship;
|
||||
data['OtherRelationship'] = this.otherRelationship;
|
||||
data['FullName'] = this.fullName;
|
||||
data['IdentificationNo'] = this.identificationNo;
|
||||
data['PatientMobileNumber'] = this.patientMobileNumber;
|
||||
data['PreferredBranchCode'] = this.preferredBranchCode;
|
||||
data['PreferredBranchName'] = this.preferredBranchName;
|
||||
if (this.medicalReportAttachment != null) {
|
||||
// FIXED: Use map() to convert each item to JSON, then convert to list
|
||||
data['MedicalReportAttachment'] = this.medicalReportAttachment!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
if (this.insuranceCardAttachment == null) {
|
||||
data['InsuranceCardAttachment'] = {};
|
||||
} else if (this.insuranceCardAttachment is EReferralAttachment) {
|
||||
// FIXED: Check if it's an EReferralAttachment before calling toJson()
|
||||
data['InsuranceCardAttachment'] = (this.insuranceCardAttachment as EReferralAttachment).toJson();
|
||||
} else {
|
||||
// If it's something else, assign directly
|
||||
data['InsuranceCardAttachment'] = this.insuranceCardAttachment;
|
||||
}
|
||||
data['VersionID'] = this.versionID;
|
||||
data['Channel'] = this.channel;
|
||||
data['LanguageID'] = this.languageID;
|
||||
data['IPAdress'] = this.iPAdress;
|
||||
data['generalid'] = this.generalid;
|
||||
data['PatientOutSA'] = this.patientOutSA;
|
||||
data['SessionID'] = this.sessionID;
|
||||
data['isDentalAllowedBackend'] = this.isDentalAllowedBackend;
|
||||
data['DeviceTypeID'] = this.deviceTypeID;
|
||||
data['PatientID'] = this.patientID;
|
||||
data['TokenID'] = this.tokenID;
|
||||
data['PatientTypeID'] = this.patientTypeID;
|
||||
data['PatientType'] = this.patientType;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class EReferralAttachment {
|
||||
String? fileName;
|
||||
String? base64String;
|
||||
|
||||
EReferralAttachment({this.fileName, this.base64String});
|
||||
|
||||
EReferralAttachment.fromJson(Map<String, dynamic> json) {
|
||||
fileName = json['FileName'];
|
||||
base64String = json['Base64String'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['FileName'] = this.fileName;
|
||||
data['Base64String'] = this.base64String;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
class SendActivationCodeForEReferralRequestModel {
|
||||
int? patientMobileNumber;
|
||||
String? zipCode;
|
||||
double? versionID;
|
||||
int? channel;
|
||||
int? languageID;
|
||||
String? iPAdress;
|
||||
String? generalid;
|
||||
int? patientOutSA;
|
||||
dynamic sessionID;
|
||||
bool? isDentalAllowedBackend;
|
||||
int? deviceTypeID;
|
||||
|
||||
SendActivationCodeForEReferralRequestModel(
|
||||
{this.patientMobileNumber,
|
||||
this.zipCode,
|
||||
this.versionID,
|
||||
this.channel,
|
||||
this.languageID,
|
||||
this.iPAdress,
|
||||
this.generalid,
|
||||
this.patientOutSA,
|
||||
this.sessionID,
|
||||
this.isDentalAllowedBackend,
|
||||
this.deviceTypeID});
|
||||
|
||||
SendActivationCodeForEReferralRequestModel.fromJson(Map<String, dynamic> json) {
|
||||
patientMobileNumber = json['PatientMobileNumber'];
|
||||
zipCode = json['ZipCode'];
|
||||
versionID = json['VersionID'];
|
||||
channel = json['Channel'];
|
||||
languageID = json['LanguageID'];
|
||||
iPAdress = json['IPAdress'];
|
||||
generalid = json['generalid'];
|
||||
patientOutSA = json['PatientOutSA'];
|
||||
sessionID = json['SessionID'];
|
||||
isDentalAllowedBackend = json['isDentalAllowedBackend'];
|
||||
deviceTypeID = json['DeviceTypeID'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['PatientMobileNumber'] = this.patientMobileNumber;
|
||||
data['ZipCode'] = this.zipCode;
|
||||
data['VersionID'] = this.versionID;
|
||||
data['Channel'] = this.channel;
|
||||
data['LanguageID'] = this.languageID;
|
||||
data['IPAdress'] = this.iPAdress;
|
||||
data['generalid'] = this.generalid;
|
||||
data['PatientOutSA'] = this.patientOutSA;
|
||||
data['SessionID'] = this.sessionID;
|
||||
data['isDentalAllowedBackend'] = this.isDentalAllowedBackend;
|
||||
data['DeviceTypeID'] = this.deviceTypeID;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
class GetAllCitiesResponseModel {
|
||||
int? iD;
|
||||
String? description;
|
||||
String? descriptionN;
|
||||
|
||||
GetAllCitiesResponseModel({this.iD, this.description, this.descriptionN});
|
||||
|
||||
GetAllCitiesResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
iD = json['ID'];
|
||||
description = json['Description'];
|
||||
descriptionN = json['DescriptionN'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['ID'] = this.iD;
|
||||
data['Description'] = this.description;
|
||||
data['DescriptionN'] = this.descriptionN;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
class GetAllRelationshipTypeResponseModel {
|
||||
int? iD;
|
||||
String? text;
|
||||
String? textAr;
|
||||
String? textEn;
|
||||
|
||||
GetAllRelationshipTypeResponseModel(
|
||||
{this.iD, this.text, this.textAr, this.textEn});
|
||||
|
||||
GetAllRelationshipTypeResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
iD = json['ID'];
|
||||
text = json['Text'];
|
||||
textAr = json['Text_Ar'];
|
||||
textEn = json['Text_En'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['ID'] = this.iD;
|
||||
data['Text'] = this.text;
|
||||
data['Text_Ar'] = this.textAr;
|
||||
data['Text_En'] = this.textEn;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
// models/referral_models.dart
|
||||
import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart';
|
||||
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.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/relationship_type_resp_mode.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
|
||||
|
||||
class ReferralFormData {
|
||||
String requesterName = '';
|
||||
String requesterPhone = '';
|
||||
CountryEnum countryEnum = CountryEnum.saudiArabia;
|
||||
GetAllRelationshipTypeResponseModel? relationship;
|
||||
String otherRelationshipName = '';
|
||||
|
||||
String patientIdentification = '';
|
||||
String patientName = '';
|
||||
String patientPhone = '';
|
||||
GetAllCitiesResponseModel? patientCity;
|
||||
|
||||
List<EReferralAttachment> medicalReportImages = [];
|
||||
HospitalsModel? branch;
|
||||
bool isPatientInsured = false;
|
||||
List<EReferralAttachment> insuredPatientImages = [];
|
||||
}
|
||||
|
||||
class FormValidationErrors {
|
||||
String? requesterName;
|
||||
String? requesterPhone;
|
||||
String? relationship;
|
||||
String? otherRelationshipName;
|
||||
|
||||
String? patientIdentification;
|
||||
String? patientName;
|
||||
String? patientCity;
|
||||
String? patientPhone;
|
||||
|
||||
|
||||
String? medicalReport;
|
||||
String? branch;
|
||||
String? insuredDocument;
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
// utils/referral_validator.dart
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart';
|
||||
|
||||
class ReferralValidator {
|
||||
static FormValidationErrors validateStep1(ReferralFormData formData) {
|
||||
final errors = FormValidationErrors();
|
||||
|
||||
if (formData.requesterName.trim().isEmpty) {
|
||||
errors.requesterName = 'Referral requester name is required';
|
||||
}
|
||||
|
||||
if (formData.requesterPhone.trim().isEmpty) {
|
||||
errors.requesterPhone = 'Phone number is required';
|
||||
} else if (formData.countryEnum.countryCode == '966' &&
|
||||
!_isValidSaudiPhone(formData.requesterPhone)) {
|
||||
errors.requesterPhone = 'Please enter a valid Saudi phone number (5xxxxxxxx)';
|
||||
}
|
||||
|
||||
if (formData.relationship == null) {
|
||||
errors.relationship = 'Please select a relationship';
|
||||
}
|
||||
|
||||
if (formData.relationship != null &&
|
||||
formData.relationship?.iD == 5 &&
|
||||
formData.otherRelationshipName.trim().isEmpty) {
|
||||
errors.otherRelationshipName = 'Other relationship name is required';
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
static FormValidationErrors validateStep2(ReferralFormData formData) {
|
||||
final errors = FormValidationErrors();
|
||||
|
||||
if (formData.patientIdentification.trim().isEmpty) {
|
||||
errors.patientIdentification = 'Identification number is required';
|
||||
}
|
||||
|
||||
if (formData.patientName.trim().isEmpty) {
|
||||
errors.patientName = 'Patient name is required';
|
||||
}
|
||||
|
||||
if (formData.patientPhone == null) {
|
||||
errors.patientPhone = 'Please Enter patient phone number';
|
||||
}
|
||||
|
||||
if (formData.patientCity == null) {
|
||||
errors.patientCity = 'Please select patient city';
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
static FormValidationErrors validateStep3(ReferralFormData formData) {
|
||||
final errors = FormValidationErrors();
|
||||
|
||||
if (formData.medicalReportImages.isEmpty) {
|
||||
errors.medicalReport = 'At least one medical report is required';
|
||||
}
|
||||
|
||||
if (formData.branch == null) {
|
||||
errors.branch = 'Please select a branch';
|
||||
}
|
||||
|
||||
if (formData.isPatientInsured && formData.insuredPatientImages.isEmpty) {
|
||||
errors.insuredDocument = 'Insurance document is required for insured patients';
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
static bool _isValidSaudiPhone(String phone) {
|
||||
final regex = RegExp(r'^5\d{8}$');
|
||||
return regex.hasMatch(phone);
|
||||
}
|
||||
|
||||
static bool hasErrors(FormValidationErrors errors) {
|
||||
return errors.requesterName != null ||
|
||||
errors.requesterPhone != null ||
|
||||
errors.relationship != null ||
|
||||
errors.otherRelationshipName != null ||
|
||||
errors.patientIdentification != null ||
|
||||
errors.patientName != null ||
|
||||
errors.patientPhone != null ||
|
||||
errors.patientCity != null ||
|
||||
errors.medicalReport != null ||
|
||||
errors.branch != null ||
|
||||
errors.insuredDocument != null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,206 @@
|
||||
// managers/referral_form_manager.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart';
|
||||
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.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/relationship_type_resp_mode.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
|
||||
|
||||
|
||||
class ReferralFormManager extends ChangeNotifier {
|
||||
final ReferralFormData _formData = ReferralFormData();
|
||||
final FormValidationErrors _errors = FormValidationErrors();
|
||||
|
||||
ReferralFormData get formData => _formData;
|
||||
FormValidationErrors get errors => _errors;
|
||||
|
||||
// Field-specific update methods that don't notify listeners immediately
|
||||
void updateRequesterName(String value) {
|
||||
_formData.requesterName = value;
|
||||
_clearError('requesterName');
|
||||
}
|
||||
|
||||
void updateRequesterPhone(String value) {
|
||||
_formData.requesterPhone = value;
|
||||
_clearError('requesterPhone');
|
||||
}
|
||||
|
||||
void updateCountryEnum(CountryEnum value) {
|
||||
_formData.countryEnum = value;
|
||||
}
|
||||
|
||||
void updateRelationship(GetAllRelationshipTypeResponseModel? value) {
|
||||
_formData.relationship = value;
|
||||
_clearError('relationship');
|
||||
}
|
||||
|
||||
void updateOtherRelationshipName(String value) {
|
||||
_formData.otherRelationshipName = value;
|
||||
_clearError('otherRelationshipName');
|
||||
}
|
||||
|
||||
void updatePatientIdentification(String value) {
|
||||
_formData.patientIdentification = value;
|
||||
_clearError('patientIdentification');
|
||||
}
|
||||
|
||||
void updatePatientName(String value) {
|
||||
_formData.patientName = value;
|
||||
_clearError('patientName');
|
||||
}
|
||||
|
||||
void updatePatientPhone(String? value) {
|
||||
_formData.patientPhone = value!;
|
||||
_clearError('patientPhone');
|
||||
}
|
||||
|
||||
void updatePatientCity(GetAllCitiesResponseModel? value) {
|
||||
_formData.patientCity = value;
|
||||
_clearError('patientCity');
|
||||
}
|
||||
|
||||
void updateBranch(HospitalsModel? value) {
|
||||
_formData.branch = value;
|
||||
_clearError('branch');
|
||||
}
|
||||
|
||||
void updateIsPatientInsured(bool value) {
|
||||
_formData.isPatientInsured = value;
|
||||
if (!value) {
|
||||
_formData.insuredPatientImages.clear();
|
||||
}
|
||||
_clearError('insuredDocument');
|
||||
}
|
||||
|
||||
void addMedicalReport(EReferralAttachment attachment) {
|
||||
_formData.medicalReportImages.add(attachment);
|
||||
_clearError('medicalReport');
|
||||
}
|
||||
|
||||
void removeMedicalReport(int index) {
|
||||
if (index >= 0 && index < _formData.medicalReportImages.length) {
|
||||
_formData.medicalReportImages.removeAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
void addInsuranceDocument(EReferralAttachment attachment) {
|
||||
_formData.insuredPatientImages.add(attachment);
|
||||
_clearError('insuredDocument');
|
||||
}
|
||||
|
||||
void removeInsuranceDocument(int index) {
|
||||
if (index >= 0 && index < _formData.insuredPatientImages.length) {
|
||||
_formData.insuredPatientImages.removeAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
// Error management
|
||||
void setErrors(FormValidationErrors newErrors) {
|
||||
_errors.requesterName = newErrors.requesterName;
|
||||
_errors.requesterPhone = newErrors.requesterPhone;
|
||||
_errors.relationship = newErrors.relationship;
|
||||
_errors.otherRelationshipName = newErrors.otherRelationshipName;
|
||||
_errors.patientIdentification = newErrors.patientIdentification;
|
||||
_errors.patientName = newErrors.patientName;
|
||||
_errors.patientPhone = newErrors.patientPhone;
|
||||
_errors.patientCity = newErrors.patientCity;
|
||||
_errors.medicalReport = newErrors.medicalReport;
|
||||
_errors.branch = newErrors.branch;
|
||||
_errors.insuredDocument = newErrors.insuredDocument;
|
||||
notifyListeners(); // Only notify when errors change
|
||||
}
|
||||
|
||||
void clearAllErrors() {
|
||||
_errors.requesterName = null;
|
||||
_errors.requesterPhone = null;
|
||||
_errors.relationship = null;
|
||||
_errors.otherRelationshipName = null;
|
||||
_errors.patientIdentification = null;
|
||||
_errors.patientName = null;
|
||||
_errors.patientPhone = null;
|
||||
_errors.patientCity = null;
|
||||
_errors.medicalReport = null;
|
||||
_errors.branch = null;
|
||||
_errors.insuredDocument = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _clearError(String field) {
|
||||
bool shouldNotify = false;
|
||||
|
||||
switch (field) {
|
||||
case 'requesterName':
|
||||
if (_errors.requesterName != null) {
|
||||
_errors.requesterName = null;
|
||||
shouldNotify = true;
|
||||
}
|
||||
break;
|
||||
case 'requesterPhone':
|
||||
if (_errors.requesterPhone != null) {
|
||||
_errors.requesterPhone = null;
|
||||
shouldNotify = true;
|
||||
}
|
||||
break;
|
||||
case 'relationship':
|
||||
if (_errors.relationship != null) {
|
||||
_errors.relationship = null;
|
||||
shouldNotify = true;
|
||||
}
|
||||
break;
|
||||
case 'otherRelationshipName':
|
||||
if (_errors.otherRelationshipName != null) {
|
||||
_errors.otherRelationshipName = null;
|
||||
shouldNotify = true;
|
||||
}
|
||||
break;
|
||||
case 'patientIdentification':
|
||||
if (_errors.patientIdentification != null) {
|
||||
_errors.patientIdentification = null;
|
||||
shouldNotify = true;
|
||||
}
|
||||
break;
|
||||
case 'patientName':
|
||||
if (_errors.patientName != null) {
|
||||
_errors.patientName = null;
|
||||
shouldNotify = true;
|
||||
}
|
||||
break;
|
||||
case 'patientPhone':
|
||||
if (_errors.patientPhone != null) {
|
||||
_errors.patientPhone = null;
|
||||
shouldNotify = true;
|
||||
}
|
||||
break;
|
||||
case 'patientCity':
|
||||
if (_errors.patientCity != null) {
|
||||
_errors.patientCity = null;
|
||||
shouldNotify = true;
|
||||
}
|
||||
break;
|
||||
case 'medicalReport':
|
||||
if (_errors.medicalReport != null) {
|
||||
_errors.medicalReport = null;
|
||||
shouldNotify = true;
|
||||
}
|
||||
break;
|
||||
case 'branch':
|
||||
if (_errors.branch != null) {
|
||||
_errors.branch = null;
|
||||
shouldNotify = true;
|
||||
}
|
||||
break;
|
||||
case 'insuredDocument':
|
||||
if (_errors.insuredDocument != null) {
|
||||
_errors.insuredDocument = null;
|
||||
shouldNotify = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (shouldNotify) {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,183 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.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/req_models/create_e_referral_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/e_referral/widget/e-referral_otp.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_other_details.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_patient_info.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_requester_form.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_stepper.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'e-referral_validator.dart';
|
||||
import 'e_referral_form_manager.dart';
|
||||
|
||||
class NewReferralPage extends StatefulWidget {
|
||||
final Function(VoidCallback) onNextStep;
|
||||
final Function(int) onStepChanged;
|
||||
|
||||
const NewReferralPage({
|
||||
super.key,
|
||||
required this.onNextStep,
|
||||
required this.onStepChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
State<NewReferralPage> createState() => _NewReferralPageState();
|
||||
}
|
||||
|
||||
class _NewReferralPageState extends State<NewReferralPage> {
|
||||
final PageController _pageController = PageController();
|
||||
final ReferralFormManager _formManager = ReferralFormManager(); // Use manager
|
||||
int _currentStep = 0;
|
||||
|
||||
final List<String> _steps = [
|
||||
'Requester Info',
|
||||
'Patient Information',
|
||||
'Other details'
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadData();
|
||||
widget.onNextStep(_handleNextStep);
|
||||
}
|
||||
|
||||
void _handleNextStep() {
|
||||
switch (_currentStep) {
|
||||
case 0:
|
||||
if (_validateCurrentStep()) {
|
||||
OTPService.openOTPScreen(
|
||||
context: context,
|
||||
phoneNumber: _formManager.formData.requesterPhone ?? '',
|
||||
countryEnum: _formManager.formData.countryEnum,
|
||||
onSuccess: _proceedToNextStep,
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (_validateCurrentStep()) {
|
||||
_proceedToNextStep();
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (_validateCurrentStep()) {
|
||||
_submitReferral();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool _validateCurrentStep() {
|
||||
FormValidationErrors stepErrors;
|
||||
|
||||
switch (_currentStep) {
|
||||
case 0:
|
||||
stepErrors = ReferralValidator.validateStep1(_formManager.formData);
|
||||
break;
|
||||
case 1:
|
||||
stepErrors = ReferralValidator.validateStep2(_formManager.formData);
|
||||
break;
|
||||
case 2:
|
||||
stepErrors = ReferralValidator.validateStep3(_formManager.formData);
|
||||
break;
|
||||
default:
|
||||
stepErrors = FormValidationErrors();
|
||||
}
|
||||
|
||||
// Update errors through manager
|
||||
_formManager.setErrors(stepErrors);
|
||||
|
||||
return !ReferralValidator.hasErrors(stepErrors);
|
||||
}
|
||||
|
||||
void _proceedToNextStep() {
|
||||
_pageController.nextPage(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
setState(() {
|
||||
_currentStep++;
|
||||
});
|
||||
widget.onStepChanged(_currentStep);
|
||||
}
|
||||
|
||||
void _submitReferral() {
|
||||
|
||||
CreateEReferralRequestModel createReferrralRequestModel =
|
||||
CreateEReferralRequestModel(
|
||||
isInsuredPatient: _formManager.formData.isPatientInsured,
|
||||
cityCode: _formManager.formData.patientCity!.iD!.toString(),
|
||||
cityName: _formManager.formData.patientCity!.description,
|
||||
requesterName: _formManager.formData.requesterName,
|
||||
requesterContactNo: _formManager.formData.requesterPhone,
|
||||
requesterRelationship: _formManager.formData.relationship?.iD,
|
||||
otherRelationship: _formManager.formData.relationship!.iD.toString(),
|
||||
fullName: _formManager.formData.patientName,
|
||||
identificationNo: int.tryParse(_formManager.formData!.patientIdentification ?? '0'),
|
||||
patientMobileNumber: _formManager.formData.patientPhone,
|
||||
preferredBranchCode: _formManager.formData.branch!.iD,
|
||||
medicalReportAttachment: _formManager.formData.medicalReportImages,
|
||||
insuranceCardAttachment: _formManager.formData.insuredPatientImages,
|
||||
preferredBranchName: _formManager.formData.branch!.desciption
|
||||
);
|
||||
|
||||
final hmgServicesVM = context.read<HmgServicesViewModel>();
|
||||
hmgServicesVM.createEReferral(
|
||||
requestModel: createReferrralRequestModel,
|
||||
onSuccess: (response) {
|
||||
print("E-Referral submitted successfully");
|
||||
},
|
||||
onError: (errorMessage) {
|
||||
// Handle error (e.g., show error message)
|
||||
print(errorMessage);
|
||||
},
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void _loadData() {
|
||||
final authVM = context.read<AuthenticationViewModel>();
|
||||
final habibWalletVM = context.read<HabibWalletViewModel>();
|
||||
final hmgServicesVM = context.read<HmgServicesViewModel>();
|
||||
|
||||
hmgServicesVM.getRelationshipType();
|
||||
authVM.loadCountriesData();
|
||||
hmgServicesVM.getAllCities();
|
||||
habibWalletVM.getProjectsList();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChangeNotifierProvider.value(
|
||||
value: _formManager,
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
ProgressStepperWidget(
|
||||
currentStep: _currentStep,
|
||||
steps: _steps,
|
||||
),
|
||||
Expanded(
|
||||
child: PageView(
|
||||
controller: _pageController,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
onPageChanged: (index) => setState(() => _currentStep = index),
|
||||
children: [
|
||||
RequesterFormStep(),
|
||||
PatientInformationStep(),
|
||||
OtherDetailsStep(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,432 +0,0 @@
|
||||
// dart
|
||||
// File: lib/presentation/e_referral/new_referral_page.dart
|
||||
|
||||
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_export.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||
import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.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/authentication/authentication_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/create_e_referral_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/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/image_picker.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class NewReferralPage extends StatefulWidget {
|
||||
final Function(VoidCallback) onNextStep;
|
||||
const NewReferralPage({super.key, required this.onNextStep});
|
||||
|
||||
@override
|
||||
State<NewReferralPage> createState() => NewReferralPageState();
|
||||
}
|
||||
|
||||
class NewReferralPageState extends State<NewReferralPage> {
|
||||
final PageController _pageController = PageController();
|
||||
int pageIndex = 0;
|
||||
int _tabIndex = 0;
|
||||
bool isPatientInsured =false;
|
||||
final TextEditingController _nameController = TextEditingController();
|
||||
final TextEditingController _phoneController = TextEditingController();
|
||||
String _country = 'Saudi Arabia';
|
||||
String? _relationship;
|
||||
List<EReferralAttachment> medicalReportImages = [];
|
||||
List<EReferralAttachment> insuredPatientImages = [];
|
||||
void nextPressed() {
|
||||
if (pageIndex < 2) {
|
||||
_pageController.nextPage(duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
|
||||
} else {
|
||||
// submit logic
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
widget.onNextStep((){
|
||||
nextPressed();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Widget _progressStep({required String title, required bool active, bool showDivider = true}) {
|
||||
final Color activeColor = active ? AppColors.primaryRedColor : Colors.grey.shade400;
|
||||
return Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 13,
|
||||
backgroundColor: active ? activeColor : Colors.grey.shade300,
|
||||
child: Icon(Icons.check, size: 14, color: Colors.white),
|
||||
),
|
||||
if (showDivider)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Divider(thickness: 1),
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(title, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600)),
|
||||
const SizedBox(height: 6),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: active ? activeColor.withOpacity(0.15) : Colors.grey.shade100,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Text(active ? 'Active' : 'Inactive',
|
||||
style: TextStyle(fontSize: 9, color: active ? activeColor : Colors.grey)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _requesterForm() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: ListView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
const Text('Referral requester information',
|
||||
style: TextStyle(fontWeight: FontWeight.w700, fontSize: 16)).paddingSymmetrical(4.h, 0.h),
|
||||
const SizedBox(height: 12),
|
||||
TextInputWidget(
|
||||
controller: _nameController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
hintText: 'Enter Referral Requester Name*', labelText: 'Requester Name',
|
||||
).paddingSymmetrical(0.h, 4.h),
|
||||
|
||||
Selector<AuthenticationViewModel, ({List<NationalityCountries>? countriesList, NationalityCountries? selectedCountry, bool isArabic})>(
|
||||
selector: (context, authViewModel) {
|
||||
final appState = getIt.get<AppState>();
|
||||
return (
|
||||
countriesList: authViewModel.countriesList,
|
||||
selectedCountry: authViewModel.pickedCountryByUAEUser,
|
||||
isArabic: appState.isArabic(),
|
||||
);
|
||||
},
|
||||
shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic,
|
||||
builder: (context, data, child) {
|
||||
final authVM = context.read<AuthenticationViewModel>();
|
||||
return DropdownWidget(
|
||||
labelText: LocaleKeys.country.tr(),
|
||||
hintText:_country,
|
||||
isEnable: true,
|
||||
dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(),
|
||||
selectedValue: data.selectedCountry != null
|
||||
? data.isArabic
|
||||
? data.selectedCountry!.nameN ?? ""
|
||||
: data.selectedCountry!.name ?? ""
|
||||
: "",
|
||||
onChange: authVM.onUAEUserCountrySelection,
|
||||
hasSelectionCustomIcon: true,
|
||||
labelColor: AppColors.textColor,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
selectionCustomIcon: AppAssets.arrow_down,
|
||||
leadingIcon: AppAssets.globe,
|
||||
).withVerticalPadding(8);
|
||||
},
|
||||
).paddingSymmetrical(0.h, 4.h),
|
||||
|
||||
TextInputWidget(
|
||||
labelText: LocaleKeys.mobileNumber.tr(),
|
||||
hintText: LocaleKeys.mobileNumber.tr(),
|
||||
controller: null,
|
||||
isEnable: true,
|
||||
prefix: null,
|
||||
isAllowLeadingIcon: true,
|
||||
labelColor: AppColors.textColor, padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
isReadOnly: true,
|
||||
leadingIcon: AppAssets.call).paddingSymmetrical(0.h, 4.h),
|
||||
|
||||
Selector<AuthenticationViewModel, ({List<NationalityCountries>? countriesList, NationalityCountries? selectedCountry, bool isArabic})>(
|
||||
selector: (context, authViewModel) {
|
||||
final appState = getIt.get<AppState>();
|
||||
return (
|
||||
countriesList: authViewModel.countriesList,
|
||||
selectedCountry: authViewModel.pickedCountryByUAEUser,
|
||||
isArabic: appState.isArabic(),
|
||||
);
|
||||
},
|
||||
shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic,
|
||||
builder: (context, data, child) {
|
||||
final authVM = context.read<AuthenticationViewModel>();
|
||||
return DropdownWidget(
|
||||
labelText: "Relationship",
|
||||
hintText: "Relationship*".needTranslation,
|
||||
isEnable: true,
|
||||
dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(),
|
||||
selectedValue: data.selectedCountry != null
|
||||
? data.isArabic
|
||||
? data.selectedCountry!.nameN ?? ""
|
||||
: data.selectedCountry!.name ?? ""
|
||||
: "",
|
||||
onChange: authVM.onUAEUserCountrySelection,
|
||||
hasSelectionCustomIcon: true,
|
||||
labelColor: AppColors.textColor,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
selectionCustomIcon: AppAssets.arrow_down,
|
||||
leadingIcon: AppAssets.globe,
|
||||
).withVerticalPadding(8);
|
||||
},
|
||||
).paddingSymmetrical(0.h, 4.h),
|
||||
|
||||
const SizedBox(height: 120),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _patientInformation(){
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: ListView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
'Patient information'.toText16(weight: FontWeight.bold).paddingSymmetrical(4.h, 0.h),
|
||||
const SizedBox(height: 12),
|
||||
TextInputWidget(
|
||||
controller: _nameController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
hintText: 'Enter Identification Number*', labelText: 'Identification Number ',
|
||||
).paddingSymmetrical(0.h, 4.h),
|
||||
|
||||
TextInputWidget(
|
||||
controller: _nameController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
hintText: 'Patient Name*', labelText: 'Name',
|
||||
).paddingSymmetrical(0.h, 4.h),
|
||||
|
||||
Selector<AuthenticationViewModel, ({List<NationalityCountries>? countriesList, NationalityCountries? selectedCountry, bool isArabic})>(
|
||||
selector: (context, authViewModel) {
|
||||
final appState = getIt.get<AppState>();
|
||||
return (
|
||||
countriesList: authViewModel.countriesList,
|
||||
selectedCountry: authViewModel.pickedCountryByUAEUser,
|
||||
isArabic: appState.isArabic(),
|
||||
);
|
||||
},
|
||||
shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic,
|
||||
builder: (context, data, child) {
|
||||
final authVM = context.read<AuthenticationViewModel>();
|
||||
return DropdownWidget(
|
||||
labelText: LocaleKeys.country.tr(),
|
||||
hintText:_country,
|
||||
isEnable: true,
|
||||
dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(),
|
||||
selectedValue: data.selectedCountry != null
|
||||
? data.isArabic
|
||||
? data.selectedCountry!.nameN ?? ""
|
||||
: data.selectedCountry!.name ?? ""
|
||||
: "",
|
||||
onChange: authVM.onUAEUserCountrySelection,
|
||||
hasSelectionCustomIcon: true,
|
||||
labelColor: AppColors.textColor,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
selectionCustomIcon: AppAssets.arrow_down,
|
||||
leadingIcon: AppAssets.globe,
|
||||
).withVerticalPadding(8);
|
||||
},
|
||||
).paddingSymmetrical(0.h, 4.h),
|
||||
|
||||
|
||||
'Where the patient located'.needTranslation.toText16(weight: FontWeight.bold).paddingSymmetrical(4.h, 0.h),
|
||||
|
||||
Selector<AuthenticationViewModel, ({List<NationalityCountries>? countriesList, NationalityCountries? selectedCountry, bool isArabic})>(
|
||||
selector: (context, authViewModel) {
|
||||
final appState = getIt.get<AppState>();
|
||||
return (
|
||||
countriesList: authViewModel.countriesList,
|
||||
selectedCountry: authViewModel.pickedCountryByUAEUser,
|
||||
isArabic: appState.isArabic(),
|
||||
);
|
||||
},
|
||||
shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic,
|
||||
builder: (context, data, child) {
|
||||
final authVM = context.read<AuthenticationViewModel>();
|
||||
return DropdownWidget(
|
||||
labelText: LocaleKeys.country.tr(),
|
||||
hintText:_country,
|
||||
isEnable: true,
|
||||
dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(),
|
||||
selectedValue: data.selectedCountry != null
|
||||
? data.isArabic
|
||||
? data.selectedCountry!.nameN ?? ""
|
||||
: data.selectedCountry!.name ?? ""
|
||||
: "",
|
||||
onChange: authVM.onUAEUserCountrySelection,
|
||||
hasSelectionCustomIcon: true,
|
||||
labelColor: AppColors.textColor,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
selectionCustomIcon: AppAssets.arrow_down,
|
||||
leadingIcon: AppAssets.globe,
|
||||
).withVerticalPadding(8);
|
||||
},
|
||||
).paddingSymmetrical(0.h, 4.h),
|
||||
|
||||
]));
|
||||
}
|
||||
|
||||
Widget _otherDetails() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: ListView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
'Other Details'.toText16(weight: FontWeight.bold).paddingSymmetrical(4.h, 0.h),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
InkWell(child: TextInputWidget(
|
||||
controller: _nameController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
hintText: 'Medical Report', labelText: 'Select Attachment',
|
||||
suffix: Icon(Icons.attachment),
|
||||
isReadOnly: true,
|
||||
|
||||
),
|
||||
onTap: (){
|
||||
ImageOptions.showImageOptionsNew(
|
||||
context,
|
||||
true,
|
||||
(String image, File file) {
|
||||
setState(() {
|
||||
EReferralAttachment eReferralAttachment = new EReferralAttachment(fileName: 'image ${medicalReportImages.length + 1}.png', base64String: image);
|
||||
medicalReportImages.add(eReferralAttachment);
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
).paddingSymmetrical(0.h, 4.h),
|
||||
|
||||
Selector<AuthenticationViewModel, ({List<NationalityCountries>? countriesList, NationalityCountries? selectedCountry, bool isArabic})>(
|
||||
selector: (context, authViewModel) {
|
||||
final appState = getIt.get<AppState>();
|
||||
return (
|
||||
countriesList: authViewModel.countriesList,
|
||||
selectedCountry: authViewModel.pickedCountryByUAEUser,
|
||||
isArabic: appState.isArabic(),
|
||||
);
|
||||
},
|
||||
shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic,
|
||||
builder: (context, data, child) {
|
||||
final authVM = context.read<AuthenticationViewModel>();
|
||||
return DropdownWidget(
|
||||
labelText: LocaleKeys.branch.tr(),
|
||||
hintText:_country,
|
||||
isEnable: true,
|
||||
dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(),
|
||||
selectedValue: data.selectedCountry != null
|
||||
? data.isArabic
|
||||
? data.selectedCountry!.nameN ?? ""
|
||||
: data.selectedCountry!.name ?? ""
|
||||
: "",
|
||||
onChange: authVM.onUAEUserCountrySelection,
|
||||
hasSelectionCustomIcon: true,
|
||||
labelColor: AppColors.textColor,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
selectionCustomIcon: AppAssets.arrow_down,
|
||||
leadingIcon: AppAssets.hospital,
|
||||
).withVerticalPadding(8);
|
||||
},
|
||||
).paddingSymmetrical(0.h, 4.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: isPatientInsured,
|
||||
activeColor: AppColors.primaryRedColor,
|
||||
onChanged: (bool? newValue) {
|
||||
setState(() {
|
||||
isPatientInsured = newValue!;
|
||||
});
|
||||
}),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Text(
|
||||
"Patient is Insured".needTranslation,
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
).paddingSymmetrical(0.h, 4.h),
|
||||
|
||||
isPatientInsured? InkWell(child: TextInputWidget(
|
||||
controller: _nameController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
hintText: 'Insurance Document', labelText: 'Select Attachment',
|
||||
suffix: Icon(Icons.attachment),
|
||||
isReadOnly: true,
|
||||
|
||||
)) : SizedBox(),
|
||||
]));
|
||||
}
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool step0Active = pageIndex == 0;
|
||||
final bool step1Active = pageIndex == 1;
|
||||
final bool step2Active = pageIndex == 2;
|
||||
|
||||
return SizedBox(
|
||||
height: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.height, // constrain height
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_progressStep(title: 'Requester Info', active: step0Active),
|
||||
_progressStep(title: 'Patient Information', active: step1Active),
|
||||
_progressStep(
|
||||
title: 'Other details', active: step2Active, showDivider: false),
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: PageView(
|
||||
controller: _pageController,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
onPageChanged: (i) => setState(() => pageIndex = i),
|
||||
children: [
|
||||
_requesterForm(),
|
||||
_patientInformation(),
|
||||
// const Center(child: Text('Patient Info - step 2 (placeholder)')),
|
||||
_otherDetails(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
|
||||
import 'package:flutter/material.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/req_models/create_e_referral_model.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/e_referral/widget/search_e_referral_form.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'e-referral_validator.dart';
|
||||
import 'e_referral_form_manager.dart';
|
||||
|
||||
class SearchEReferralPage extends StatefulWidget {
|
||||
final Function(VoidCallback) onNextStep;
|
||||
|
||||
|
||||
const SearchEReferralPage({
|
||||
super.key,
|
||||
required this.onNextStep,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SearchEReferralPage> createState() => _SearchEReferralPageState();
|
||||
}
|
||||
|
||||
class _SearchEReferralPageState extends State<SearchEReferralPage> {
|
||||
final PageController _pageController = PageController();
|
||||
final ReferralFormManager _formManager = ReferralFormManager(); // Use manager
|
||||
int _currentStep = 0;
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadData();
|
||||
widget.onNextStep(_handleNextStep);
|
||||
}
|
||||
|
||||
void _handleNextStep() {
|
||||
_searchReferral();
|
||||
}
|
||||
|
||||
void _searchReferral() {
|
||||
|
||||
CreateEReferralRequestModel createReferrralRequestModel =
|
||||
CreateEReferralRequestModel(
|
||||
isInsuredPatient: _formManager.formData.isPatientInsured,
|
||||
cityCode: _formManager.formData.patientCity!.iD!.toString(),
|
||||
cityName: _formManager.formData.patientCity!.description,
|
||||
requesterName: _formManager.formData.requesterName,
|
||||
requesterContactNo: _formManager.formData.requesterPhone,
|
||||
requesterRelationship: _formManager.formData.relationship?.iD,
|
||||
otherRelationship: _formManager.formData.relationship!.iD.toString(),
|
||||
fullName: _formManager.formData.patientName,
|
||||
identificationNo: int.tryParse(_formManager.formData!.patientIdentification ?? '0'),
|
||||
patientMobileNumber: _formManager.formData.patientPhone,
|
||||
preferredBranchCode: _formManager.formData.branch!.iD,
|
||||
medicalReportAttachment: _formManager.formData.medicalReportImages,
|
||||
insuranceCardAttachment: _formManager.formData.insuredPatientImages,
|
||||
preferredBranchName: _formManager.formData.branch!.desciption
|
||||
);
|
||||
|
||||
final hmgServicesVM = context.read<HmgServicesViewModel>();
|
||||
hmgServicesVM.createEReferral(
|
||||
requestModel: createReferrralRequestModel,
|
||||
onSuccess: (response) {
|
||||
print("E-Referral submitted successfully");
|
||||
},
|
||||
onError: (errorMessage) {
|
||||
// Handle error (e.g., show error message)
|
||||
print(errorMessage);
|
||||
},
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void _loadData() {
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChangeNotifierProvider.value(
|
||||
value: _formManager,
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Column(
|
||||
children: [
|
||||
|
||||
Expanded(
|
||||
child:
|
||||
SearchEReferralFormForm(),
|
||||
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
// services/otp_service.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.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/req_models/check_activation_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/widgets/common_bottom_sheet.dart';
|
||||
import 'package:hmg_patient_app_new/features/authentication/widgets/otp_verification_screen.dart';
|
||||
|
||||
class OTPService {
|
||||
static void openOTPScreen({
|
||||
required BuildContext context,
|
||||
required String phoneNumber,
|
||||
required CountryEnum countryEnum,
|
||||
required Function onSuccess,
|
||||
}) {
|
||||
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
|
||||
hmgServicesViewModel.eReferralSendActivationCode(
|
||||
requestModel: SendActivationCodeForEReferralRequestModel(
|
||||
patientMobileNumber: int.parse(phoneNumber),
|
||||
zipCode: countryEnum.countryCode,
|
||||
patientOutSA: countryEnum.countryCode == '966' ? 0 : 1,
|
||||
),
|
||||
onSuccess: (GenericApiModel response) {
|
||||
_showOTPVerificationSheet(
|
||||
context: context,
|
||||
phoneNumber: phoneNumber,
|
||||
loginTokenID: response.data,
|
||||
onSuccess: onSuccess,
|
||||
);
|
||||
},
|
||||
onError: (String errorMessage) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(errorMessage))
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static void _showOTPVerificationSheet({
|
||||
required BuildContext context,
|
||||
required String phoneNumber,
|
||||
required String loginTokenID,
|
||||
required Function onSuccess,
|
||||
}) {
|
||||
showCommonBottomSheet(
|
||||
context,
|
||||
isFullScreen: true,
|
||||
title: "OTP Verification",
|
||||
isCloseButtonVisible: false,
|
||||
height:ResponsiveExtension.screenHeight * 0.75,
|
||||
child: OTPVerificationScreen(
|
||||
phoneNumber: phoneNumber,
|
||||
checkActivationCode: (int code) {
|
||||
_verifyOTP(
|
||||
context: context,
|
||||
loginTokenID: loginTokenID,
|
||||
code: code,
|
||||
onSuccess: onSuccess,
|
||||
);
|
||||
},
|
||||
onResendOTPPressed: (String phoneNumber) {
|
||||
Navigator.pop(context);
|
||||
openOTPScreen(
|
||||
context: context,
|
||||
phoneNumber: phoneNumber,
|
||||
countryEnum: CountryEnum.saudiArabia,
|
||||
onSuccess: onSuccess,
|
||||
);
|
||||
},
|
||||
isFormFamilyFile: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static void _verifyOTP({
|
||||
required BuildContext context,
|
||||
required String loginTokenID,
|
||||
required int code,
|
||||
required Function onSuccess,
|
||||
}) {
|
||||
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
|
||||
hmgServicesViewModel.checkEReferralActivationCode(
|
||||
requestModel: CheckActivationCodeForEReferralRequestModel(
|
||||
logInTokenID: loginTokenID,
|
||||
activationCode: code.toString(),
|
||||
),
|
||||
onSuccess: (GenericApiModel response) {
|
||||
Navigator.pop(context);
|
||||
onSuccess();
|
||||
},
|
||||
onError: (String errorMessage) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(errorMessage))
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,308 @@
|
||||
// widgets/other_details_step.dart
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/validation_utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/image_picker.dart';
|
||||
|
||||
|
||||
class OtherDetailsStep extends StatefulWidget {
|
||||
const OtherDetailsStep({super.key});
|
||||
|
||||
@override
|
||||
State<OtherDetailsStep> createState() => _OtherDetailsStepState();
|
||||
}
|
||||
|
||||
class _OtherDetailsStepState extends State<OtherDetailsStep> {
|
||||
final TextEditingController _medicalReportController = TextEditingController();
|
||||
final TextEditingController _insuranceController = TextEditingController();
|
||||
|
||||
late ReferralFormManager _formManager;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_formManager = context.read<ReferralFormManager>();
|
||||
_updateMedicalReportText();
|
||||
_updateInsuranceText();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(OtherDetailsStep oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
_updateMedicalReportText();
|
||||
_updateInsuranceText();
|
||||
}
|
||||
|
||||
void _updateMedicalReportText() {
|
||||
final hasMedicalReports = _formManager.formData.medicalReportImages.isNotEmpty;
|
||||
_medicalReportController.text = hasMedicalReports
|
||||
? '${_formManager.formData.medicalReportImages.length} file(s) selected'
|
||||
: '';
|
||||
}
|
||||
|
||||
void _updateInsuranceText() {
|
||||
final hasInsuranceDocs = _formManager.formData.insuredPatientImages.isNotEmpty;
|
||||
_insuranceController.text = hasInsuranceDocs
|
||||
? '${_formManager.formData.insuredPatientImages.length} file(s) selected'
|
||||
: '';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<ReferralFormManager>(
|
||||
builder: (context, formManager, child) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: ListView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
_buildSectionTitle('Other Details'),
|
||||
const SizedBox(height: 12),
|
||||
_buildMedicalReportField(formManager),
|
||||
_buildBranchField(context, formManager),
|
||||
_buildInsuranceCheckbox(formManager),
|
||||
if (formManager.formData.isPatientInsured) _buildInsuranceField(formManager),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSectionTitle(String title) {
|
||||
return Text(
|
||||
title,
|
||||
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 16)
|
||||
).paddingSymmetrical(4, 0);
|
||||
}
|
||||
|
||||
Widget _buildMedicalReportField(ReferralFormManager formManager) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
InkWell(
|
||||
child: TextInputWidget(
|
||||
controller: _medicalReportController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
hintText: 'Medical Report',
|
||||
labelText: 'Select Attachment',
|
||||
suffix: const Icon(Icons.attachment),
|
||||
isReadOnly: true,
|
||||
errorMessage: formManager.errors.medicalReport,
|
||||
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.medicalReport),
|
||||
),
|
||||
onTap: () {
|
||||
ImageOptions.showImageOptionsNew(
|
||||
context,
|
||||
true,
|
||||
(String image, File file) {
|
||||
_addMedicalReport(image, file, formManager);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
if (formManager.formData.medicalReportImages.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
child: Wrap(
|
||||
spacing: 8.0,
|
||||
children: formManager.formData.medicalReportImages.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
return Chip(
|
||||
label: Text('Medical Report ${index + 1}'),
|
||||
deleteIcon: const Icon(Icons.close, size: 16),
|
||||
onDeleted: () {
|
||||
_removeMedicalReport(index, formManager);
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
).paddingSymmetrical(0, 4);
|
||||
}
|
||||
|
||||
Widget _buildBranchField(BuildContext context, ReferralFormManager formManager) {
|
||||
return DropdownWidget(
|
||||
labelText: 'Branch',
|
||||
hintText: formManager.formData.branch?.desciption ?? "Select Branch",
|
||||
isEnable: false,
|
||||
hasSelectionCustomIcon: true,
|
||||
labelColor: Colors.black,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
selectionCustomIcon: AppAssets.arrow_down,
|
||||
leadingIcon: AppAssets.hospital,
|
||||
dropdownItems: [],
|
||||
errorMessage: formManager.errors.branch,
|
||||
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.branch),
|
||||
).paddingSymmetrical(0, 4).onPress(() {
|
||||
_showBranchBottomSheet(context, formManager);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildInsuranceCheckbox(ReferralFormManager formManager) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: formManager.formData.isPatientInsured,
|
||||
activeColor: Colors.red,
|
||||
onChanged: (bool? newValue) {
|
||||
final value = newValue ?? false;
|
||||
formManager.updateIsPatientInsured(value);
|
||||
if (!value) {
|
||||
_updateInsuranceText();
|
||||
}
|
||||
},
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(5.0),
|
||||
child: Text(
|
||||
"Patient is Insured",
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
).paddingSymmetrical(0, 4);
|
||||
}
|
||||
|
||||
Widget _buildInsuranceField(ReferralFormManager formManager) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
InkWell(
|
||||
child: TextInputWidget(
|
||||
controller: _insuranceController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
hintText: 'Insurance Document',
|
||||
labelText: 'Select Attachment',
|
||||
suffix: const Icon(Icons.attachment),
|
||||
isReadOnly: true,
|
||||
errorMessage: formManager.errors.insuredDocument,
|
||||
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.insuredDocument),
|
||||
),
|
||||
onTap: () {
|
||||
ImageOptions.showImageOptionsNew(
|
||||
context,
|
||||
true,
|
||||
(String image, File file) {
|
||||
_addInsuranceDocument(image, file, formManager);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
if (formManager.formData.insuredPatientImages.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
child: Wrap(
|
||||
spacing: 8.0,
|
||||
children: formManager.formData.insuredPatientImages.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
return Chip(
|
||||
label: Text('Insurance ${index + 1}'),
|
||||
deleteIcon: const Icon(Icons.close, size: 16),
|
||||
onDeleted: () {
|
||||
_removeInsuranceDocument(index, formManager);
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _showBranchBottomSheet(BuildContext context, ReferralFormManager formManager) {
|
||||
final habibWalletVM = context.read<HabibWalletViewModel>();
|
||||
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
title: "Select Branch",
|
||||
child: Consumer<HabibWalletViewModel>(
|
||||
builder: (context, habibWalletVM, child) {
|
||||
final hospitals = habibWalletVM.advancePaymentHospitals;
|
||||
if (hospitals == null || hospitals.isEmpty) {
|
||||
return const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Text('No branches available'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
final branch = hospitals[index];
|
||||
return ListTile(
|
||||
title: Text(branch.desciption ?? 'Unknown'),
|
||||
onTap: () {
|
||||
formManager.updateBranch(branch);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => const Divider(),
|
||||
itemCount: hospitals.length,
|
||||
);
|
||||
},
|
||||
),
|
||||
useSafeArea: true,
|
||||
isFullScreen: false,
|
||||
isCloseButtonVisible: true,
|
||||
);
|
||||
}
|
||||
|
||||
void _addMedicalReport(String image, File file, ReferralFormManager formManager) {
|
||||
final newAttachment = EReferralAttachment(
|
||||
fileName: 'medical_report_${formManager.formData.medicalReportImages.length + 1}.png',
|
||||
base64String: image
|
||||
);
|
||||
|
||||
formManager.addMedicalReport(newAttachment);
|
||||
_updateMedicalReportText();
|
||||
}
|
||||
|
||||
void _removeMedicalReport(int index, ReferralFormManager formManager) {
|
||||
formManager.removeMedicalReport(index);
|
||||
_updateMedicalReportText();
|
||||
}
|
||||
|
||||
void _addInsuranceDocument(String image, File file, ReferralFormManager formManager) {
|
||||
final newAttachment = EReferralAttachment(
|
||||
fileName: 'insurance_${formManager.formData.insuredPatientImages.length + 1}.png',
|
||||
base64String: image
|
||||
);
|
||||
|
||||
formManager.addInsuranceDocument(newAttachment);
|
||||
_updateInsuranceText();
|
||||
}
|
||||
|
||||
void _removeInsuranceDocument(int index, ReferralFormManager formManager) {
|
||||
formManager.removeInsuranceDocument(index);
|
||||
_updateInsuranceText();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_medicalReportController.dispose();
|
||||
_insuranceController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,289 @@
|
||||
// widgets/patient_information_step.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/validation_utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
|
||||
class PatientInformationStep extends StatefulWidget {
|
||||
const PatientInformationStep({super.key});
|
||||
|
||||
@override
|
||||
State<PatientInformationStep> createState() => PatientInformationStepState();
|
||||
}
|
||||
|
||||
class PatientInformationStepState extends State<PatientInformationStep> {
|
||||
late TextEditingController _identificationController;
|
||||
late TextEditingController _nameController;
|
||||
late TextEditingController _phoneController;
|
||||
|
||||
late FocusNode _identificationFocusNode;
|
||||
late FocusNode _nameFocusNode;
|
||||
late FocusNode _phoneFocusNode;
|
||||
|
||||
late ReferralFormManager _formManager;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_formManager = context.read<ReferralFormManager>();
|
||||
|
||||
_identificationController = TextEditingController();
|
||||
_nameController = TextEditingController();
|
||||
_phoneController = TextEditingController();
|
||||
|
||||
_identificationFocusNode = FocusNode();
|
||||
_nameFocusNode = FocusNode();
|
||||
_phoneFocusNode = FocusNode();
|
||||
|
||||
// Initialize controllers with current values
|
||||
_identificationController.text = _formManager.formData.patientIdentification ?? '';
|
||||
_nameController.text = _formManager.formData.patientName ?? '';
|
||||
_phoneController.text = _formManager.formData.patientPhone ?? '';
|
||||
|
||||
// Auto-focus the identification field when the step loads
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
_identificationFocusNode.requestFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<ReferralFormManager>(
|
||||
builder: (context, formManager, child) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: ListView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
_buildSectionTitle('Patient information'),
|
||||
const SizedBox(height: 12),
|
||||
_buildIdentificationField(formManager),
|
||||
_buildPatientNameField(formManager),
|
||||
// _buildPatientCountryField(context, formManager),
|
||||
_buildPatientPhoneField(formManager),
|
||||
const SizedBox(height: 20),
|
||||
_buildSectionTitle('Where the patient located'),
|
||||
_buildPatientCityField(context, formManager),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSectionTitle(String title) {
|
||||
return Text(
|
||||
title,
|
||||
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 16)
|
||||
).paddingSymmetrical(4, 0);
|
||||
}
|
||||
|
||||
Widget _buildIdentificationField(ReferralFormManager formManager) {
|
||||
return Focus(
|
||||
focusNode: _identificationFocusNode,
|
||||
child: TextInputWidget(
|
||||
controller: _identificationController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
hintText: 'Enter Identification Number*',
|
||||
labelText: 'Identification Number',
|
||||
errorMessage: formManager.errors.patientIdentification,
|
||||
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientIdentification),
|
||||
onChange: (value) {
|
||||
formManager.updatePatientIdentification(value ?? '');
|
||||
},
|
||||
onSubmitted: (value) {
|
||||
_nameFocusNode.requestFocus();
|
||||
},
|
||||
).paddingSymmetrical(0, 4),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPatientNameField(ReferralFormManager formManager) {
|
||||
return Focus(
|
||||
focusNode: _nameFocusNode,
|
||||
child: TextInputWidget(
|
||||
controller: _nameController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
hintText: 'Patient Name*',
|
||||
labelText: 'Name',
|
||||
keyboardType: TextInputType.text,
|
||||
errorMessage: formManager.errors.patientName,
|
||||
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientName),
|
||||
onChange: (value) {
|
||||
formManager.updatePatientName(value ?? '');
|
||||
},
|
||||
onSubmitted: (value) {
|
||||
// Optionally move to next field or keep focus
|
||||
},
|
||||
).paddingSymmetrical(0, 4),
|
||||
);
|
||||
}
|
||||
|
||||
// Widget _buildPatientCountryField(BuildContext context, ReferralFormManager formManager) {
|
||||
// return DropdownWidget(
|
||||
// labelText: 'Country',
|
||||
// hintText: formManager.formData.patientCountry?.name ?? "Select Country",
|
||||
// isEnable: false,
|
||||
// hasSelectionCustomIcon: true,
|
||||
// labelColor: Colors.black,
|
||||
// padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
// selectionCustomIcon: AppAssets.arrow_down,
|
||||
// leadingIcon: AppAssets.globe,
|
||||
// dropdownItems: [],
|
||||
// errorMessage: formManager.errors.patientCountry,
|
||||
// hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientCountry),
|
||||
// ).paddingSymmetrical(0, 4).onPress(() {
|
||||
// _showCountryBottomSheet(context, formManager);
|
||||
// });
|
||||
// }
|
||||
|
||||
Widget _buildPatientPhoneField(ReferralFormManager formManager) {
|
||||
return Focus(
|
||||
focusNode: _phoneFocusNode,
|
||||
child: TextInputWidget(
|
||||
labelText: 'Phone Number',
|
||||
hintText: "5xxxxxxxx",
|
||||
controller: _phoneController,
|
||||
padding: const EdgeInsets.all(8),
|
||||
keyboardType: TextInputType.number,
|
||||
onChange: (value) {
|
||||
formManager.updatePatientPhone(value ?? '');
|
||||
},
|
||||
onCountryChange: (value) {
|
||||
formManager.updateCountryEnum(value);
|
||||
},
|
||||
prefix: '966',
|
||||
isBorderAllowed: false,
|
||||
isAllowLeadingIcon: true,
|
||||
fontSize: 13,
|
||||
isCountryDropDown: true,
|
||||
leadingIcon: AppAssets.smart_phone,
|
||||
errorMessage: formManager.errors.patientPhone,
|
||||
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientPhone)
|
||||
).paddingSymmetrical(0, 8),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPatientCityField(BuildContext context, ReferralFormManager formManager) {
|
||||
return DropdownWidget(
|
||||
labelText: 'City',
|
||||
hintText: formManager.formData.patientCity?.description ?? "Select City",
|
||||
isEnable: false,
|
||||
hasSelectionCustomIcon: true,
|
||||
labelColor: Colors.black,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
selectionCustomIcon: AppAssets.arrow_down,
|
||||
leadingIcon: AppAssets.globe,
|
||||
dropdownItems: [],
|
||||
errorMessage: formManager.errors.patientCity,
|
||||
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientCity),
|
||||
).paddingSymmetrical(0, 4).onPress(() {
|
||||
_showCityBottomSheet(context, formManager);
|
||||
});
|
||||
}
|
||||
|
||||
// void _showCountryBottomSheet(BuildContext context, ReferralFormManager formManager) {
|
||||
// final authVM = context.read<AuthenticationViewModel>();
|
||||
//
|
||||
// showCommonBottomSheetWithoutHeight(
|
||||
// context,
|
||||
// title: "Select Country",
|
||||
// child: Consumer<AuthenticationViewModel>(
|
||||
// builder: (context, authVM, child) {
|
||||
// final countries = authVM.countriesList;
|
||||
// if (countries == null || countries.isEmpty) {
|
||||
// return const Center(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.all(16.0),
|
||||
// child: Text('No countries available'),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// return ListView.separated(
|
||||
// shrinkWrap: true,
|
||||
// physics: const BouncingScrollPhysics(),
|
||||
// itemBuilder: (context, index) {
|
||||
// final country = countries[index];
|
||||
// return ListTile(
|
||||
// title: Text(country.name ?? 'Unknown'),
|
||||
// onTap: () {
|
||||
// formManager.updatePatientCountry(country);
|
||||
// Navigator.pop(context);
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// separatorBuilder: (context, index) => const Divider(),
|
||||
// itemCount: countries.length,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// useSafeArea: true,
|
||||
// isFullScreen: false,
|
||||
// isCloseButtonVisible: true,
|
||||
// );
|
||||
// }
|
||||
|
||||
void _showCityBottomSheet(BuildContext context, ReferralFormManager formManager) {
|
||||
final hmgServicesVM = context.read<HmgServicesViewModel>();
|
||||
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
title: "Select City",
|
||||
child: Consumer<HmgServicesViewModel>(
|
||||
builder: (context, hmgServicesVM, child) {
|
||||
final cities = hmgServicesVM.getAllCitiesList;
|
||||
if (cities == null || cities.isEmpty) {
|
||||
return const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Text('No cities available'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
final city = cities[index];
|
||||
return ListTile(
|
||||
title: Text(city.description ?? 'Unknown'),
|
||||
onTap: () {
|
||||
formManager.updatePatientCity(city);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => const Divider(),
|
||||
itemCount: cities.length,
|
||||
);
|
||||
},
|
||||
),
|
||||
useSafeArea: true,
|
||||
isFullScreen: false,
|
||||
isCloseButtonVisible: true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_identificationController.dispose();
|
||||
_nameController.dispose();
|
||||
_phoneController.dispose();
|
||||
_identificationFocusNode.dispose();
|
||||
_nameFocusNode.dispose();
|
||||
_phoneFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,228 @@
|
||||
// widgets/requester_form_step.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/validation_utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/e_referral/e-referral_validator.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
// widgets/requester_form_step.dart
|
||||
// widgets/requester_form_step.dart
|
||||
class RequesterFormStep extends StatefulWidget {
|
||||
const RequesterFormStep({super.key});
|
||||
|
||||
@override
|
||||
State<RequesterFormStep> createState() => RequesterFormStepState();
|
||||
}
|
||||
|
||||
class RequesterFormStepState extends State<RequesterFormStep> {
|
||||
late TextEditingController _nameController;
|
||||
late TextEditingController _phoneController;
|
||||
late TextEditingController _otherNameController;
|
||||
|
||||
late FocusNode _nameFocusNode;
|
||||
late FocusNode _phoneFocusNode;
|
||||
late FocusNode _otherNameFocusNode;
|
||||
|
||||
late ReferralFormManager _formManager;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_formManager = context.read<ReferralFormManager>();
|
||||
|
||||
_nameController = TextEditingController();
|
||||
_phoneController = TextEditingController();
|
||||
_otherNameController = TextEditingController();
|
||||
|
||||
_nameFocusNode = FocusNode();
|
||||
_phoneFocusNode = FocusNode();
|
||||
_otherNameFocusNode = FocusNode();
|
||||
|
||||
// Initialize controllers with current values
|
||||
_nameController.text = _formManager.formData.requesterName ?? '';
|
||||
_phoneController.text = _formManager.formData.requesterPhone ?? '';
|
||||
_otherNameController.text = _formManager.formData.otherRelationshipName ?? '';
|
||||
|
||||
// Auto-focus the name field when the step loads
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
_nameFocusNode.requestFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<ReferralFormManager>(
|
||||
builder: (context, formManager, child) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: ListView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
_buildSectionTitle('Referral requester information'),
|
||||
const SizedBox(height: 12),
|
||||
_buildNameField(formManager),
|
||||
_buildPhoneField(formManager),
|
||||
_buildRelationshipField(context, formManager),
|
||||
if (_showOtherNameField(formManager)) _buildOtherNameField(formManager),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSectionTitle(String title) {
|
||||
return Text(
|
||||
title,
|
||||
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 16)
|
||||
).paddingSymmetrical(4, 0);
|
||||
}
|
||||
|
||||
Widget _buildNameField(ReferralFormManager formManager) {
|
||||
return Focus(
|
||||
focusNode: _nameFocusNode,
|
||||
child: TextInputWidget(
|
||||
controller: _nameController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
hintText: 'Enter Referral Requester Name*',
|
||||
labelText: 'Requester Name',
|
||||
keyboardType: TextInputType.text,
|
||||
errorMessage: formManager.errors.requesterName,
|
||||
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterName),
|
||||
onChange: (value) {
|
||||
formManager.updateRequesterName(value ?? '');
|
||||
},
|
||||
onSubmitted: (value) {
|
||||
_phoneFocusNode.requestFocus();
|
||||
},
|
||||
).paddingSymmetrical(0, 8),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPhoneField(ReferralFormManager formManager) {
|
||||
return Focus(
|
||||
focusNode: _phoneFocusNode,
|
||||
child: TextInputWidget(
|
||||
labelText: 'Phone Number',
|
||||
hintText: "5xxxxxxxx",
|
||||
controller: _phoneController,
|
||||
padding: const EdgeInsets.all(8),
|
||||
keyboardType: TextInputType.number,
|
||||
onChange: (value) {
|
||||
formManager.updateRequesterPhone(value ?? '');
|
||||
},
|
||||
onCountryChange: (value) {
|
||||
formManager.updateCountryEnum(value);
|
||||
},
|
||||
prefix: '966',
|
||||
isBorderAllowed: false,
|
||||
isAllowLeadingIcon: true,
|
||||
fontSize: 13,
|
||||
isCountryDropDown: true,
|
||||
leadingIcon: AppAssets.smart_phone,
|
||||
errorMessage: formManager.errors.requesterPhone,
|
||||
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterPhone)
|
||||
).paddingSymmetrical(0, 8),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRelationshipField(BuildContext context, ReferralFormManager formManager) {
|
||||
return DropdownWidget(
|
||||
labelText: "Relationship",
|
||||
hintText: formManager.formData.relationship?.textEn ?? "Select Relation",
|
||||
isEnable: false,
|
||||
selectedValue: formManager.formData.relationship?.textEn ?? "Select Relation",
|
||||
errorMessage: formManager.errors.relationship,
|
||||
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.relationship),
|
||||
hasSelectionCustomIcon: false,
|
||||
labelColor: Colors.black,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
leadingIcon: AppAssets.user_circle,
|
||||
dropdownItems: [],
|
||||
).paddingSymmetrical(0, 8).onPress(() {
|
||||
_showRelationshipBottomSheet(context, formManager);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildOtherNameField(ReferralFormManager formManager) {
|
||||
return Focus(
|
||||
focusNode: _otherNameFocusNode,
|
||||
child: TextInputWidget(
|
||||
controller: _otherNameController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
hintText: 'Other Name*',
|
||||
labelText: 'Other Name',
|
||||
errorMessage: formManager.errors.otherRelationshipName,
|
||||
onChange: (value) {
|
||||
formManager.updateOtherRelationshipName(value ?? '');
|
||||
},
|
||||
).paddingSymmetrical(0, 4),
|
||||
);
|
||||
}
|
||||
|
||||
bool _showOtherNameField(ReferralFormManager formManager) {
|
||||
return formManager.formData.relationship != null &&
|
||||
formManager.formData.relationship?.iD == 5;
|
||||
}
|
||||
|
||||
void _showRelationshipBottomSheet(BuildContext context, ReferralFormManager formManager) {
|
||||
final hmgServicesVM = context.read<HmgServicesViewModel>();
|
||||
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
title: "Select Relation",
|
||||
child: Consumer<HmgServicesViewModel>(
|
||||
builder: (context, hmgServicesVM, child) {
|
||||
if (hmgServicesVM.relationTypes.isEmpty) {
|
||||
return const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Text('No relationships available'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
final relationship = hmgServicesVM.relationTypes[index];
|
||||
return ListTile(
|
||||
title: Text(relationship.textEn ?? 'Unknown'),
|
||||
onTap: () {
|
||||
formManager.updateRelationship(relationship);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => const Divider(),
|
||||
itemCount: hmgServicesVM.relationTypes.length,
|
||||
);
|
||||
},
|
||||
),
|
||||
isFullScreen: false,
|
||||
isCloseButtonVisible: true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nameController.dispose();
|
||||
_phoneController.dispose();
|
||||
_otherNameController.dispose();
|
||||
_nameFocusNode.dispose();
|
||||
_phoneFocusNode.dispose();
|
||||
_otherNameFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
// widgets/progress_stepper_widget.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
|
||||
class ProgressStepperWidget extends StatelessWidget {
|
||||
final int currentStep;
|
||||
final List<String> steps;
|
||||
|
||||
const ProgressStepperWidget({
|
||||
super.key,
|
||||
required this.currentStep,
|
||||
required this.steps,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
for (int i = 0; i < steps.length; i++)
|
||||
_buildStep(
|
||||
title: steps[i],
|
||||
active: i == currentStep,
|
||||
showDivider: i < steps.length - 1,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStep({required String title, required bool active, bool showDivider = true}) {
|
||||
final Color activeColor = active ? AppColors.primaryRedColor : Colors.grey.shade400;
|
||||
|
||||
return Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 13,
|
||||
backgroundColor: active ? activeColor : Colors.grey.shade300,
|
||||
child: Icon(Icons.check, size: 14, color: Colors.white),
|
||||
),
|
||||
if (showDivider)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Divider(thickness: 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(title, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600)),
|
||||
const SizedBox(height: 6),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: active ? activeColor.withOpacity(0.15) : Colors.grey.shade100,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Text(
|
||||
active ? 'Active' : 'Inactive',
|
||||
style: TextStyle(fontSize: 9, color: active ? activeColor : Colors.grey)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,184 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/validation_utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/e_referral/e-referral_validator.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
class SearchEReferralFormForm extends StatefulWidget {
|
||||
const SearchEReferralFormForm({super.key});
|
||||
|
||||
@override
|
||||
State<SearchEReferralFormForm> createState() => SearchEReferralFormFormState();
|
||||
}
|
||||
|
||||
class SearchEReferralFormFormState extends State<SearchEReferralFormForm> {
|
||||
late TextEditingController _searchController;
|
||||
late TextEditingController _phoneController;
|
||||
late FocusNode _searchFocusNode;
|
||||
late FocusNode _phoneFocusNode;
|
||||
|
||||
int criteria =0;
|
||||
|
||||
List<Map<int,String>> criteriaList = [
|
||||
{0: 'Identification Number'},
|
||||
{1: 'Referral Number'},
|
||||
];
|
||||
late ReferralFormManager _formManager;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_formManager = context.read<ReferralFormManager>();
|
||||
|
||||
_searchController = TextEditingController();
|
||||
_phoneController = TextEditingController();
|
||||
_searchFocusNode = FocusNode();
|
||||
_phoneFocusNode = FocusNode();
|
||||
|
||||
// Initialize controllers with current values
|
||||
_searchController.text = '';
|
||||
_phoneController.text = '';
|
||||
|
||||
// Auto-focus the name field when the step loads
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
_searchFocusNode.requestFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<ReferralFormManager>(
|
||||
builder: (context, formManager, child) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: ListView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
children: [
|
||||
|
||||
const SizedBox(height: 12),
|
||||
_buildSelectionField(context, formManager),
|
||||
_buildNameField(formManager),
|
||||
_buildPhoneField(formManager),
|
||||
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Widget _buildNameField(ReferralFormManager formManager) {
|
||||
return Focus(
|
||||
focusNode: _searchFocusNode,
|
||||
child: TextInputWidget(
|
||||
controller: _searchController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
hintText:criteria ==0 ? "Enter Identification Number" : "Enter Referral Number",
|
||||
labelText: criteria ==0 ? "Identification Number" : "Referral Number",
|
||||
keyboardType: TextInputType.text,
|
||||
// errorMessage: formManager.errors.requesterName,
|
||||
hasError: !ValidationUtils.isNullOrEmpty(_searchController.text),
|
||||
onChange: (value) {
|
||||
|
||||
},
|
||||
onSubmitted: (value) {
|
||||
|
||||
},
|
||||
).paddingSymmetrical(0, 8),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPhoneField(ReferralFormManager formManager) {
|
||||
return Focus(
|
||||
focusNode: _phoneFocusNode,
|
||||
child: TextInputWidget(
|
||||
labelText: 'Phone Number',
|
||||
hintText: "5xxxxxxxx",
|
||||
controller: _phoneController,
|
||||
padding: const EdgeInsets.all(8),
|
||||
keyboardType: TextInputType.number,
|
||||
onChange: (value) {
|
||||
formManager.updateRequesterPhone(value ?? '');
|
||||
},
|
||||
onCountryChange: (value) {
|
||||
formManager.updateCountryEnum(value);
|
||||
},
|
||||
prefix: '966',
|
||||
isBorderAllowed: false,
|
||||
isAllowLeadingIcon: true,
|
||||
fontSize: 13,
|
||||
isCountryDropDown: true,
|
||||
leadingIcon: AppAssets.smart_phone,
|
||||
errorMessage: formManager.errors.requesterPhone,
|
||||
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterPhone)
|
||||
).paddingSymmetrical(0, 8),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSelectionField(BuildContext context, ReferralFormManager formManager) {
|
||||
return DropdownWidget(
|
||||
labelText: "Select the Search Criteria",
|
||||
hintText: criteria ==0 ? "Identification Number" : "Referral Number",
|
||||
isEnable: false,
|
||||
hasSelectionCustomIcon: false,
|
||||
labelColor: Colors.black,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
leadingIcon: AppAssets.search_icon,
|
||||
dropdownItems: [],
|
||||
).paddingSymmetrical(0, 8).onPress(() {
|
||||
_showCriteriaBottomSheet(context);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _showCriteriaBottomSheet(BuildContext context,) {
|
||||
|
||||
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
title: "Select Criteria",
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
return ListTile(
|
||||
title: Text(criteriaList[index].values.first),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
criteria = index;
|
||||
});
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => const Divider(),
|
||||
itemCount: criteriaList.length,
|
||||
),
|
||||
isFullScreen: false,
|
||||
isCloseButtonVisible: true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.dispose();
|
||||
_phoneController.dispose();
|
||||
_searchFocusNode.dispose();
|
||||
_phoneFocusNode.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue