Compare commits

...

9 Commits

Author SHA1 Message Date
Haroon6138 a3d89c7afa Merge pull request 'dev_sultan' (#104) from dev_sultan into master
Reviewed-on: #104
5 days ago
Sultan khan 9e0336687c virtual tour. 6 days ago
Sultan khan 2532b494f7 e-referral done. 6 days ago
Sultan khan a4d3bb3533 Merge branch 'master' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New into dev_sultan 6 days ago
Sultan khan fb69723a65 e-referral done. 6 days ago
Sultan khan 5f047e1b6f Merge branch 'master' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New into dev_sultan
# Conflicts:
#	lib/core/api_consts.dart
#	lib/features/hmg_services/hmg_services_repo.dart
#	lib/features/hmg_services/hmg_services_view_model.dart
#	lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart
1 week ago
Sultan khan d8a9d8443a e-referral work done search-referral in progress. 1 week ago
Sultan khan 9a04974db3 Merge branch 'faiz_dev' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New into dev_sultan
# Conflicts:
#	lib/core/api_consts.dart
2 weeks ago
Sultan khan c43f96c619 e-referral work in progress 2 weeks ago

@ -14,8 +14,8 @@ var PACKAGES_ORDERS = '/api/orders';
var PACKAGES_ORDER_HISTORY = '/api/orders/items';
var PACKAGES_TAMARA_OPT = '/api/orders/paymentoptions/tamara';
// var BASE_URL = 'http://10.50.100.198:2018/';
// var BASE_URL = 'https://uat.hmgwebservices.com/';
var BASE_URL = 'https://hmgwebservices.com/';
var BASE_URL = 'https://uat.hmgwebservices.com/';
// var BASE_URL = 'https://hmgwebservices.com/';
// var BASE_URL = 'http://10.201.204.103/';
// var BASE_URL = 'https://orash.cloudsolutions.com.sa/';
// var BASE_URL = 'https://vidauat.cloudsolutions.com.sa/';
@ -178,8 +178,12 @@ var DELETE_CHILD_REQUEST = 'Services/Community.svc/REST/DeleteBaby';
var GET_TABLE_REQUEST = 'Services/Community.svc/REST/CreateVaccinationTable';
///BloodDenote
///
/// use get all cities from the e-referral, already calling this api there don't use multiple same api calls
var GET_CITIES_REQUEST = 'Services/Lists.svc/REST/GetAllCities';
///
///
///
///BloodDetails
var GET_BLOOD_REQUEST = 'services/PatientVarification.svc/REST/BloodDonation_GetBloodGroupDetails';
@ -440,12 +444,7 @@ var H2O_UPDATE_USER_DETAIL = "Services/H2ORemainder.svc/REST/H2O_UpdateUserDetai
var H2O_UNDO_USER_ACTIVITY = "Services/H2ORemainder.svc/REST/H2o_UndoUserActivity";
//E_Referral Services
var GET_ALL_RELATIONSHIP_TYPES = "Services/Patients.svc/REST/GetAllRelationshipTypes";
var SEND_ACTIVATION_CODE_FOR_E_REFERRAL = 'Services/Authentication.svc/REST/SendActivationCodeForEReferral';
var CHECK_ACTIVATION_CODE_FOR_E_REFERRAL = 'Services/Authentication.svc/REST/CheckActivationCodeForEReferral';
var GET_ALL_CITIES = 'services/Lists.svc/rest/GetAllCities';
var CREATE_E_REFERRAL = "Services/Patients.svc/REST/CreateEReferral";
var GET_E_REFERRALS = "Services/Patients.svc/REST/GetEReferrals";
// Encillary Orders
@ -711,7 +710,7 @@ var GET_PRESCRIPTION_INSTRUCTIONS_PDF = 'Services/ChatBot_Service.svc/REST/Chatb
class ApiConsts {
static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT
@ -851,6 +850,17 @@ class ApiConsts {
static final String updateHHCOrder = 'api/hhc/update';
static final String addHHCOrder = 'api/HHC/add';
//E-REFERRAL SERVICES
static final getAllRelationshipTypes = "Services/Patients.svc/REST/GetAllRelationshipTypes";
static final sendActivationCodeForEReferral = 'Services/Authentication.svc/REST/SendActivationCodeForEReferral';
static final checkActivationCodeForEReferral = 'Services/Authentication.svc/REST/CheckActivationCodeForEReferral';
static final getAllCities = 'services/Lists.svc/rest/GetAllCities';
static final createEReferral = "Services/Patients.svc/REST/CreateEReferral";
static final getEReferrals = "Services/Patients.svc/REST/GetEReferrals";
// ************ static values for Api ****************
static final double appVersionID = 19.3;
static final int appChannelId = 3;

@ -221,7 +221,7 @@ class AppDependencies {
);
getIt.registerLazySingleton<HmgServicesViewModel>(
() => HmgServicesViewModel(bookAppointmentsRepo: getIt(), hmgServicesRepo: getIt(), errorHandlerService: getIt()),
() => HmgServicesViewModel(bookAppointmentsRepo: getIt(), hmgServicesRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()),
);
// Screen-specific VMs Factory

@ -8,6 +8,7 @@ import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:crypto/crypto.dart' as crypto;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:google_api_availability/google_api_availability.dart';
@ -29,6 +30,7 @@ import 'package:hmg_patient_app_new/widgets/loading_dialog.dart';
import 'package:lottie/lottie.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
class Utils {
static AppState appState = getIt.get<AppState>();
@ -865,4 +867,8 @@ class Utils {
}
return isHavePrivilege;
}
static void openWebView({ required String url}) {
Uri uri = Uri.parse(url);
launchUrl(uri, mode: LaunchMode.inAppBrowserView);
}
}

@ -101,8 +101,7 @@ class ValidationUtils {
return regex.hasMatch(id);
}
static bool validateUaeRegistration(
{String? name, GenderTypeEnum? gender, NationalityCountries? country, MaritalStatusTypeEnum? maritalStatus, required Function() onOkPress}) {
static bool validateUaeRegistration({String? name, GenderTypeEnum? gender, NationalityCountries? country, MaritalStatusTypeEnum? maritalStatus, required Function() onOkPress}) {
if (name == null || name.isEmpty) {
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAValidName.tr(), onOkPressed: onOkPress);
return false;
@ -141,8 +140,7 @@ class ValidationUtils {
return true;
}
static bool isValidatedIdAndPhoneWithCountryValidation(
{String? nationalId, String? phoneNumber, required Function() onOkPress, CountryEnum? selectedCountry}) {
static bool isValidatedIdAndPhoneWithCountryValidation({String? nationalId, String? phoneNumber, required Function() onOkPress, CountryEnum? selectedCountry}) {
bool isCorrectID = true;
if (nationalId == null || nationalId.isEmpty) {
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAnationalID.tr(), onOkPressed: onOkPress);
@ -171,4 +169,10 @@ class ValidationUtils {
}
return isCorrectID;
}
}
static bool isNullOrEmpty(String? value) {
return value == null || value
.trim()
.isEmpty;
}
}

@ -5,12 +5,21 @@ import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.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/cmc_create_new_order_req_model.dart';
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/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/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:provider/provider.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/relationship_type_resp_mode.dart';
import 'models/resq_models/search_e_referral_resp_model.dart';
abstract class HmgServicesRepo {
Future<Either<Failure, GenericApiModel<List<GetCMCAllOrdersResponseModel>>>> getAllComprehensiveCheckupOrders();
@ -38,6 +47,21 @@ abstract class HmgServicesRepo {
required int orderServiceID,
required List<PatientERCMCInsertServicesList> services,
});
Future<Either<Failure, GenericApiModel<List<GetAllRelationshipTypeResponseModel>>>> getRelationshipTypes();
Future<Either<Failure, GenericApiModel<List<GetAllCitiesResponseModel>>>> getAllCities();
Future<Either<Failure, GenericApiModel<String>>> sendEReferralActivationCode(SendActivationCodeForEReferralRequestModel requestModel);
Future<Either<Failure, GenericApiModel<String>>> checkEReferralActivationCode(CheckActivationCodeForEReferralRequestModel requestModel);
Future<Either<Failure, GenericApiModel<String>>> createEReferral(CreateEReferralRequestModel requestModel);
Future<Either<Failure, GenericApiModel<List<SearchEReferralResponseModel>>>> searchEReferral(SearchEReferralRequestModel requestModel);
}
class HmgServicesRepoImp implements HmgServicesRepo {
@ -519,4 +543,278 @@ class HmgServicesRepoImp implements HmgServicesRepo {
return Left(UnknownFailure(e.toString()));
}
}
/*e-referral functions*/
@override
Future<Either<Failure, GenericApiModel<List<GetAllRelationshipTypeResponseModel>>>> getRelationshipTypes() async {
Map<String, dynamic> requestBody = {};
try {
GenericApiModel<List<GetAllRelationshipTypeResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getAllRelationshipTypes,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<GetAllRelationshipTypeResponseModel> relationshipList = [];
if (response['List_EReferralResult'] != null && response['List_EReferralResult'] is List) {
final servicesList = response['List_EReferralResult'] as List;
for (var serviceJson in servicesList) {
if (serviceJson is Map<String, dynamic>) {
relationshipList.add(GetAllRelationshipTypeResponseModel.fromJson(serviceJson));
}
}
}
apiResponse = GenericApiModel<List<GetAllRelationshipTypeResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: relationshipList,
);
} 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 getAllCmcServices: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<GetAllCitiesResponseModel>>>> getAllCities() async {
Map<String, dynamic> requestBody = {};
try {
GenericApiModel<List<GetAllCitiesResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getAllCities,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<GetAllCitiesResponseModel> citiesList = [];
if (response['ListCities'] != null && response['ListCities'] is List) {
final servicesList = response['ListCities'] as List;
for (var serviceJson in servicesList) {
if (serviceJson is Map<String, dynamic>) {
citiesList.add(GetAllCitiesResponseModel.fromJson(serviceJson));
}
}
}
apiResponse = GenericApiModel<List<GetAllCitiesResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: citiesList,
);
} 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 getAllCmcServices: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<String>>> sendEReferralActivationCode(SendActivationCodeForEReferralRequestModel requestModel) async {
GenericApiModel<String>? apiResponse;
try {
Failure? failure;
await apiClient.post(
ApiConsts.sendActivationCodeForEReferral,
body: requestModel.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<String>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response['LogInTokenID'] as String,
);
} 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 getAllCmcServices: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<String>>> createEReferral(CreateEReferralRequestModel requestModel) async {
GenericApiModel<String>? apiResponse;
try {
Failure? failure;
await apiClient.post(
ApiConsts.createEReferral,
body: requestModel.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<String>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response['ReferralNumber'].toString(),
);
} 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 getAllCmcServices: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<String>>> checkEReferralActivationCode(CheckActivationCodeForEReferralRequestModel requestModel) async {
GenericApiModel<String>? apiResponse;
try {
Failure? failure;
await apiClient.post(
ApiConsts.checkActivationCodeForEReferral,
body: requestModel.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<String>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response['IsAuthenticated'].toString(),
);
} 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 getAllCmcServices: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<SearchEReferralResponseModel>>>> searchEReferral(SearchEReferralRequestModel requestBody) async {
try {
GenericApiModel<List<SearchEReferralResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getEReferrals,
body: requestBody.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<SearchEReferralResponseModel> searchReferral = [];
if (response['List_EReferrals'] != null && response['List_EReferrals'] is List) {
final servicesList = response['List_EReferrals'] as List;
for (var serviceJson in servicesList) {
if (serviceJson is Map<String, dynamic>) {
searchReferral.add(SearchEReferralResponseModel.fromJson(serviceJson));
}
}
}
apiResponse = GenericApiModel<List<SearchEReferralResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: searchReferral,
);
} 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()));
}
}
}

@ -1,19 +1,31 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.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/req_models/cmc_create_new_order_req_model.dart';
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/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/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/relationship_type_resp_mode.dart';
class HmgServicesViewModel extends ChangeNotifier {
final HmgServicesRepo hmgServicesRepo;
final BookAppointmentsRepo bookAppointmentsRepo;
final ErrorHandlerService errorHandlerService;
final NavigationService navigationService;
HmgServicesViewModel({required this.bookAppointmentsRepo, required this.hmgServicesRepo, required this.errorHandlerService, required this.navigationService});
HmgServicesViewModel({required this.bookAppointmentsRepo, required this.hmgServicesRepo, required this.errorHandlerService});
bool isCmcOrdersLoading = false;
bool isCmcServicesLoading = false;
@ -41,6 +53,12 @@ class HmgServicesViewModel extends ChangeNotifier {
// HHC order creation state (no hospital selection needed for home healthcare)
GetCMCServicesResponseModel? selectedServiceForHhcOrder;
List<GetAllRelationshipTypeResponseModel> relationTypes =[];
List<GetAllCitiesResponseModel> getAllCitiesList =[];
List<SearchEReferralResponseModel> searchReferralList =[];
Future<void> getOrdersList() async {}
// HHC multiple services selection
List<GetCMCServicesResponseModel> selectedHhcServices = [];
@ -512,4 +530,262 @@ class HmgServicesViewModel extends ChangeNotifier {
selectedHhcServices.clear();
notifyListeners();
}
/*e-referral functions*/
Future<void> getRelationshipType({
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
notifyListeners();
final result = await hmgServicesRepo.getRelationshipTypes();
result.fold(
(failure) async {
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 1) {
relationTypes = apiResponse.data ?? [];
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<void> getAllCities({
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
notifyListeners();
final result = await hmgServicesRepo.getAllCities();
result.fold(
(failure) async {
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 1) {
getAllCitiesList = apiResponse.data ?? [];
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<void> eReferralSendActivationCode({
required SendActivationCodeForEReferralRequestModel requestModel,
Function(GenericApiModel)? onSuccess,
Function(String)? onError,
}) async {
notifyListeners();
final result = await hmgServicesRepo.sendEReferralActivationCode(requestModel);
result.fold(
(failure) async {
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<void> checkEReferralActivationCode({
required CheckActivationCodeForEReferralRequestModel requestModel,
Function(GenericApiModel)? onSuccess,
Function(String)? onError,
}) async {
notifyListeners();
final result = await hmgServicesRepo.checkEReferralActivationCode(requestModel);
result.fold(
(failure) async {
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<void> createEReferral({
required CreateEReferralRequestModel requestModel,
Function(GenericApiModel)? onSuccess,
Function(String)? onError,
}) async {
notifyListeners();
final result = await hmgServicesRepo.createEReferral(requestModel);
result.fold(
(failure) async {
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<void> searchEReferral({
required SearchEReferralRequestModel requestModel,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
notifyListeners();
final result = await hmgServicesRepo.searchEReferral(requestModel);
result.fold(
(failure) async {
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 1) {
searchReferralList = apiResponse.data ?? [];
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<void> navigateToOTPScreen(
{required OTPTypeEnum otpTypeEnum,
required String phoneNumber,
required String loginToken,
required Function onSuccess,
}) async {
navigationService.pushToOtpScreen(
phoneNumber: phoneNumber,
isFormFamilyFile: false,
checkActivationCode: (int activationCode) async {
checkEReferralActivationCode(
requestModel: CheckActivationCodeForEReferralRequestModel(
logInTokenID: loginToken,
activationCode: activationCode.toString(),
),
onSuccess: (GenericApiModel response) {
onSuccess();
},
onError: (String errorMessage) {
print(errorMessage);
},
);
},
onResendOTPPressed: (String phoneNumber) async {
// await sendActivationCode(
// otpTypeEnum: otpTypeEnum,
// phoneNumber: phoneNumberController.text,
// nationalIdOrFileNumber: nationalIdController.text,
// isForRegister: isComingFromRegister,
// isComingFromResendOTP: true,
// payload: payload,
// isFormFamilyFile: isFormFamilyFile,
// isExcludedUser: isExcludedUser,
// responseID: responseID,
// );
},
);
}
}

@ -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,150 @@
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) {
data['MedicalReportAttachment'] = this.medicalReportAttachment!.map((v) => v.toJson()).toList();
}
if (this.insuranceCardAttachment == null) {
data['InsuranceCardAttachment'] = {};
} else
data['InsuranceCardAttachment'] = this.insuranceCardAttachment.toJson();
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,60 @@
class SearchEReferralRequestModel {
String? patientMobileNumber;
double? versionID;
int? channel;
int? languageID;
String? iPAdress;
String? generalid;
int? patientOutSA;
dynamic sessionID;
bool? isDentalAllowedBackend;
int? deviceTypeID;
int? referralNumber;
String? identificationNo;
SearchEReferralRequestModel(
{this.patientMobileNumber,
this.versionID,
this.channel,
this.languageID,
this.iPAdress,
this.generalid,
this.patientOutSA,
this.sessionID,
this.isDentalAllowedBackend,
this.deviceTypeID,
this.referralNumber,
this.identificationNo});
SearchEReferralRequestModel.fromJson(Map<String, dynamic> json) {
patientMobileNumber = json['PatientMobileNumber'];
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'];
referralNumber = json['ReferralNumber'];
identificationNo = json['IdentificationNo'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['PatientMobileNumber'] = this.patientMobileNumber;
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['ReferralNumber'] = this.referralNumber;
data['IdentificationNo'] = this.identificationNo;
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,161 @@
import 'dart:convert';
class SearchEReferralResponseModel {
dynamic acceptedBrachCode;
dynamic acceptedBranchName;
dynamic acceptedBranchNameAr;
dynamic channel;
dynamic identityCardAttachment;
String? identityNumber;
dynamic insuranceCardAttachment;
bool? isInsuredPatient;
List<MedicalReportAttachment>? medicalReportAttachment;
String? otherRelationship;
String? patientContactNo;
int? patientId;
String? patientName;
dynamic preferredBranchCode;
dynamic preferredBranchName;
String? referralDate;
int? referralNumber;
RelationshipType? relationshipType;
String? requesterContactNo;
String? requesterName;
String? status;
String? statusAr;
SearchEReferralResponseModel({
this.acceptedBrachCode,
this.acceptedBranchName,
this.acceptedBranchNameAr,
this.channel,
this.identityCardAttachment,
this.identityNumber,
this.insuranceCardAttachment,
this.isInsuredPatient,
this.medicalReportAttachment,
this.otherRelationship,
this.patientContactNo,
this.patientId,
this.patientName,
this.preferredBranchCode,
this.preferredBranchName,
this.referralDate,
this.referralNumber,
this.relationshipType,
this.requesterContactNo,
this.requesterName,
this.status,
this.statusAr,
});
factory SearchEReferralResponseModel.fromRawJson(String str) => SearchEReferralResponseModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory SearchEReferralResponseModel.fromJson(Map<String, dynamic> json) => SearchEReferralResponseModel(
acceptedBrachCode: json["AcceptedBrachCode"],
acceptedBranchName: json["AcceptedBranchName"],
acceptedBranchNameAr: json["AcceptedBranchNameAr"],
channel: json["Channel"],
identityCardAttachment: json["IdentityCardAttachment"],
identityNumber: json["IdentityNumber"],
insuranceCardAttachment: json["InsuranceCardAttachment"],
isInsuredPatient: json["IsInsuredPatient"],
medicalReportAttachment: json["MedicalReportAttachment"] == null ? [] : List<MedicalReportAttachment>.from(json["MedicalReportAttachment"]!.map((x) => MedicalReportAttachment.fromJson(x))),
otherRelationship: json["OtherRelationship"],
patientContactNo: json["PatientContactNo"],
patientId: json["PatientId"],
patientName: json["PatientName"],
preferredBranchCode: json["PreferredBranchCode"],
preferredBranchName: json["PreferredBranchName"],
referralDate: json["ReferralDate"],
referralNumber: json["ReferralNumber"],
relationshipType: json["RelationshipType"] == null ? null : RelationshipType.fromJson(json["RelationshipType"]),
requesterContactNo: json["RequesterContactNo"],
requesterName: json["RequesterName"],
status: json["Status"],
statusAr: json["StatusAr"],
);
Map<String, dynamic> toJson() => {
"AcceptedBrachCode": acceptedBrachCode,
"AcceptedBranchName": acceptedBranchName,
"AcceptedBranchNameAr": acceptedBranchNameAr,
"Channel": channel,
"IdentityCardAttachment": identityCardAttachment,
"IdentityNumber": identityNumber,
"InsuranceCardAttachment": insuranceCardAttachment,
"IsInsuredPatient": isInsuredPatient,
"MedicalReportAttachment": medicalReportAttachment == null ? [] : List<dynamic>.from(medicalReportAttachment!.map((x) => x.toJson())),
"OtherRelationship": otherRelationship,
"PatientContactNo": patientContactNo,
"PatientId": patientId,
"PatientName": patientName,
"PreferredBranchCode": preferredBranchCode,
"PreferredBranchName": preferredBranchName,
"ReferralDate": referralDate,
"ReferralNumber": referralNumber,
"RelationshipType": relationshipType?.toJson(),
"RequesterContactNo": requesterContactNo,
"RequesterName": requesterName,
"Status": status,
"StatusAr": statusAr,
};
}
class MedicalReportAttachment {
String? base64String;
String? fileName;
MedicalReportAttachment({
this.base64String,
this.fileName,
});
factory MedicalReportAttachment.fromRawJson(String str) => MedicalReportAttachment.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory MedicalReportAttachment.fromJson(Map<String, dynamic> json) => MedicalReportAttachment(
base64String: json["Base64String"],
fileName: json["FileName"],
);
Map<String, dynamic> toJson() => {
"Base64String": base64String,
"FileName": fileName,
};
}
class RelationshipType {
int? id;
String? text;
String? textAr;
String? textEn;
RelationshipType({
this.id,
this.text,
this.textAr,
this.textEn,
});
factory RelationshipType.fromRawJson(String str) => RelationshipType.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory RelationshipType.fromJson(Map<String, dynamic> json) => RelationshipType(
id: json["ID"],
text: json["Text"],
textAr: json["Text_Ar"],
textEn: json["Text_En"],
);
Map<String, dynamic> toJson() => {
"ID": id,
"Text": text,
"Text_Ar": textAr,
"Text_En": textEn,
};
}

@ -0,0 +1,48 @@
// 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 = '';
String searchPhone ='';
CountryEnum countryEnum = CountryEnum.saudiArabia;
CountryEnum patientCountryEnum = 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;
String? searchValue;
String? searchPhone;
}

@ -5,21 +5,26 @@ class HmgServicesComponentModel {
String title;
String subTitle;
String icon;
Color? iconColor;
bool isLogin;
bool isLocked;
Color bgColor;
Color textColor;
String route;
String? route;
Function? onTap;
HmgServicesComponentModel(
this.action,
this.title,
this.subTitle,
this.icon,
this.isLogin, {
this.isLocked = false,
this.bgColor = Colors.white,
this.textColor = Colors.black,
this.route = '',
this.iconColor = Colors.white,
this.route,
this.onTap
});
}

@ -0,0 +1,85 @@
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'.needTranslation;
}
if (formData.relationship == null) {
errors.relationship = 'Please select a relationship'.needTranslation;
}
if (formData.relationship != null &&
formData.relationship?.iD == 5 &&
formData.otherRelationshipName.trim().isEmpty) {
errors.otherRelationshipName = 'Other relationship name is required'.needTranslation;
}
return errors;
}
static FormValidationErrors validateStep2(ReferralFormData formData) {
final errors = FormValidationErrors();
if (formData.patientIdentification.trim().isEmpty) {
errors.patientIdentification = 'Identification number is required'.needTranslation;
}
if (formData.patientName.trim().isEmpty) {
errors.patientName = 'Patient name is required'.needTranslation;
}
if (formData.patientPhone == null) {
errors.patientPhone = 'Please Enter patient phone number'.needTranslation;
}
if (formData.patientCity == null) {
errors.patientCity = 'Please select patient city'.needTranslation;
}
return errors;
}
static FormValidationErrors validateStep3(ReferralFormData formData) {
final errors = FormValidationErrors();
if (formData.medicalReportImages.isEmpty) {
errors.medicalReport = 'At least one medical report is required'.needTranslation;
}
if (formData.branch == null) {
errors.branch = 'Please select a branch'.needTranslation;
}
if (formData.isPatientInsured && formData.insuredPatientImages.isEmpty) {
errors.insuredDocument = 'Insurance document is required for insured patients'.needTranslation;
}
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,290 @@
// 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;
int _searchCriteria = 0; // 0 = Identification Number, 1 = Referral Number
String? _searchValue;
String? _searchPhone;
// Getters
int get searchCriteria => _searchCriteria;
String? get searchValue => _searchValue;
String? get searchPhone => _searchPhone;
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 updatePatientCountryEnum(CountryEnum value) {
_formData.patientCountryEnum = value;
}
void updateRelationship(GetAllRelationshipTypeResponseModel? value) {
_formData.relationship = value;
notifyListeners();
_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');
notifyListeners();
}
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);
}
notifyListeners();
}
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);
}
notifyListeners();
}
// 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();
}
}
void updateSearchCriteria(int criteria) {
_searchCriteria = criteria;
_errors.searchValue = _validateSearchValue();
notifyListeners();
}
void updateSearchValue(String value) {
_searchValue = value;
_errors.searchValue = _validateSearchValue();
}
void updateSearchPhone(String value) {
_searchPhone = value;
_errors.searchPhone = _validateSearchPhone();
}
String? _validateSearchValue() {
if (_searchValue == null || _searchValue!.isEmpty) {
return _searchCriteria == 0
? 'Identification Number is required'
: 'Referral Number is required';
}
if (_searchCriteria == 0) {
// Validate identification number format
if (!_isValidIdentificationNumber(_searchValue!)) {
return 'Please enter a valid identification number';
}
} else {
// Validate referral number format
if (!_isValidReferralNumber(_searchValue!)) {
return 'Please enter a valid referral number';
}
}
return null;
}
bool _isValidIdentificationNumber(String value) {
// Add your identification number validation logic
return value.isNotEmpty; // Basic validation
}
bool _isValidReferralNumber(String value) {
// Add your referral number validation logic
return value.isNotEmpty; // Basic validation
}
void validateSearchForm() {
_errors.searchValue = _validateSearchValue();
_errors.searchPhone = _validateSearchPhone();
notifyListeners();
}
String? _validateSearchPhone() {
if (_searchPhone == null || _searchPhone!.isEmpty) {
return 'Phone number is required';
}
return null;
}
bool get isSearchFormInValid {
return (
_errors.searchValue != null &&
_errors.searchValue!.isNotEmpty ||
_errors.searchPhone != null &&
_errors.searchPhone!.isNotEmpty);
}
}

@ -1,97 +0,0 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.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/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/new_referral.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:provider/provider.dart';
class EReferralPage extends StatefulWidget {
const EReferralPage({super.key});
@override
_EReferralPageState createState() => _EReferralPageState();
}
class _EReferralPageState extends State<EReferralPage>
{
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
bool isNewReferral = true;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title:"E Referral".needTranslation,
child: SingleChildScrollView(
child: Consumer<PrescriptionsViewModel>(builder: (context, model, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
Row(
children: [
CustomButton(
text: "New Referral".needTranslation,
onPressed: () {
isNewReferral =true;
setState(() {
});
},
backgroundColor: model.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2),
textColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
SizedBox(width: 8.h),
CustomButton(
text: "Search Referral".needTranslation,
onPressed: () {
isNewReferral =false;
},
backgroundColor: model.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
borderColor: model.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor,
textColor: model.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 20.h),
isNewReferral ? NewEReferral() : SizedBox(),
],
);
}),
),
),
);
}
}

@ -0,0 +1,280 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.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/features/hmg_services/hmg_services_view_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/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:provider/provider.dart';
import 'package:smooth_corner/smooth_corner.dart';
class SearchResultPage extends StatefulWidget {
const SearchResultPage({super.key});
@override
_SearchResultPageState createState() => _SearchResultPageState();
}
class _SearchResultPageState extends State<SearchResultPage> {
HmgServicesViewModel? hmgServicesVM;
String _selectedFilter = 'All';
@override
void initState() {
hmgServicesVM = context.read<HmgServicesViewModel>();
super.initState();
}
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: "Search Result".needTranslation,
child: Column(
children: [
// List of referrals
ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: hmgServicesVM?.searchReferralList.length,
itemBuilder: (context, index) {
return _buildReferralCard(hmgServicesVM!.searchReferralList[index]);
},
),
],
),
);
}
Widget _buildReferralCard(SearchEReferralResponseModel referral) {
return SmoothCard(
borderRadius: BorderRadius.circular(16.h),
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
color: AppColors.whiteColor,
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
'Referral No ${referral.referralNumber}'.needTranslation.toText18(isBold: true, color: AppColors.textColor),
Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: _getStatusColor(referral.status!),
borderRadius: BorderRadius.circular(20),
),
child:
referral.status!.toText12(color: AppColors.whiteColor,
// style: TextStyle(
// color: Colors.white,
// fontSize: 12,
// fontWeight: FontWeight.w500,
// ),
),
),
],
),
SizedBox(height: 16),
// Patient information
Row(
children: [
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: AppColors.lightGrayBGColor,
shape: BoxShape.circle,
),
child: Icon(
Icons.person,
color: AppColors.lightGrayBGColor,
size: 30,
),
),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//Text(
referral.patientName!.toText16(isBold: true, color: AppColors.textColor),
// style: TextStyle(
// fontSize: 16,
// fontWeight: FontWeight.bold,
// ),
// ),
SizedBox(height: 4),
// Text(
'ID: ${referral.identityNumber}'.toText14(color: AppColors.greyTextColor),
// style: TextStyle(
// color: Colors.grey[600],
// ),
// ),
],
),
),
],
),
SizedBox(height: 16),
// Details row
Row(
children: [
_buildDetailItem(
Icons.phone,
referral.patientContactNo!,
),
SizedBox(width: 16),
_buildDetailItem(Icons.calendar_today, Utils.getDayMonthYearDateFormatted(DateUtil.convertStringToDateNoTimeZone(referral.referralDate!))),
],
),
SizedBox(height: 16),
// Requester information
Container(
padding: EdgeInsets.all(5.h),
decoration: BoxDecoration(
color: AppColors.lightGrayBGColor,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Icon(
Icons.person_outline,
color: Colors.grey[600],
size: 20,
),
SizedBox(width: 8),
//Text(
'Requester: ${referral.requesterName}'.toText14(),
// style: TextStyle(
// color: Colors.grey[700],
// ),
// ),
SizedBox(width: 16),
Icon(
Icons.phone,
color: Colors.grey[600],
size: 16,
),
SizedBox(width: 4),
// Text(
referral.requesterContactNo!.toText14(),
// style: TextStyle(
// color: Colors.grey[700],
// ),
// ),
],
),
),
SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(
Icons.group,
color: Colors.grey[600],
size: 16,
),
SizedBox(width: 4),
//Text(
'Relationship: ${referral.relationshipType?.text}'.toText14(color:AppColors.greyTextColor),
// style: TextStyle(
// color: Colors.grey[700],
// fontSize: 14,
// ),
// ),
],
),
Row(
children: [
Icon(
Icons.attach_file,
color: Colors.grey[600],
size: 16,
),
SizedBox(width: 4),
// Text(
'${referral.medicalReportAttachment?.length} file(s)'.toText14(color:AppColors.greyTextColor),
// style: TextStyle(
// color: Colors.grey[700],
// fontSize: 14,
// ),
// ),
],
),
],
),
SizedBox(height: 16),
// Action buttons
],
),
),
);
}
Widget _buildDetailItem(IconData icon, String text) {
return Row(
children: [
Icon(
icon,
color: Colors.grey[600],
size: 16,
),
SizedBox(width: 4),
text.toText14(color: AppColors.greyTextColor),
],
);
}
Color _getStatusColor(String status) {
switch (status) {
case 'Pending':
return AppColors.alertColor;
case 'Completed':
return AppColors.bgGreenColor;
case 'Rejected':
return AppColors.primaryRedColor;
default:
return AppColors.lightGrayColor;
}
}
Widget _buildFilterOption(String filter) {
return ListTile(
leading: Icon(
_selectedFilter == filter ? Icons.radio_button_checked : Icons.radio_button_off,
color: _selectedFilter == filter ? Colors.blue[700] : Colors.grey,
),
title: Text(filter),
onTap: () {
setState(() {
_selectedFilter = filter;
});
Navigator.pop(context);
},
);
}
}

@ -0,0 +1,262 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/route_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/search_e_referral.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/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:hmg_patient_app_new/widgets/stepper/stepper_widget.dart';
import 'package:provider/provider.dart';
import 'e-referral_validator.dart';
import 'e_referral_form_manager.dart';
class NewReferralPage extends StatefulWidget {
const NewReferralPage({
super.key,
});
@override
State<NewReferralPage> createState() => _NewReferralPageState();
}
class _NewReferralPageState extends State<NewReferralPage> {
final PageController _pageController = PageController();
final ReferralFormManager _formManager = ReferralFormManager(); // Use manager
int _currentStep = 0;
double widthOfOneState = ((ResponsiveExtension.screenWidth) / 3) - (20.h);
@override
void initState() {
super.initState();
_loadData();
}
void _handleNextStep() {
switch (_currentStep) {
case 0:
if (_validateCurrentStep()) {
OTPService.openOTPScreen(
context: context,
formManager: _formManager,
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 createReferralRequestModel = CreateEReferralRequestModel(
isInsuredPatient: _formManager.formData.isPatientInsured,
cityCode: _formManager.formData.patientCity!.iD!.toString(),
cityName: _formManager.formData.patientCity!.description,
requesterName: _formManager.formData.requesterName,
requesterContactNo: _formManager.formData.countryEnum.countryCode + _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.patientCountryEnum.countryCode + _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>();
LoaderBottomSheet.showLoader();
hmgServicesVM.createEReferral(
requestModel: createReferralRequestModel,
onSuccess: (GenericApiModel response) {
showSuccessBottomSheet(int.parse(response.data), hmgServicesVM);
LoaderBottomSheet.hideLoader();
},
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 Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: "E Referral".needTranslation,
isClose: false,
search: () async {
await Navigator.of(context).push(
CustomPageRoute(
page: SearchEReferralPage(),
fullScreenDialog: true,
direction: AxisDirection.down,
),
);
},
bottomChild: Container(
color: Colors.white,
padding: EdgeInsets.all(ResponsiveExtension(20).h),
child: CustomButton(
text: _currentStep <=1 ? LocaleKeys.next.tr() : LocaleKeys.submit.tr(),
// icon: AppAssets.search_icon,
iconColor: Colors.white,
onPressed: () => {_handleNextStep()},
),
),
child: ChangeNotifierProvider.value(
value: _formManager,
child: SizedBox(
height: ResponsiveExtension.screenHeight * 0.65,
child: Column(
children: [
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(3, (index) {
if (_currentStep == index) {
return StepperWidget(widthOfOneState, AppColors.primaryRedColor, true, 4.h);
} else {
return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, 4.h);
}
})),
Expanded(
child: PageView(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
onPageChanged: (index) => {
// setState(() => _currentStep = index)
},
children: [
RequesterFormStep(),
PatientInformationStep(),
OtherDetailsStep(),
],
)),
],
)),
)));
// );
}
showSuccessBottomSheet(int requestId, HmgServicesViewModel hmgServicesViewModel) {
return showCommonBottomSheetWithoutHeight(
context,
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
children: [
Utils.getSuccessWidget(loadingText: "Your Referral has been created Successfully.".needTranslation),
Row(
children: [
"Here is your Referral #: ".needTranslation.toText14(
color: AppColors.textColorLight,
weight: FontWeight.w500,
),
SizedBox(width: 4.w),
("$requestId").toText16(isBold: true),
],
),
SizedBox(height: 24.h),
Row(
children: [
Expanded(
child: CustomButton(
height: 56.h,
text: LocaleKeys.ok.tr(),
onPressed: () {
context.pop();
context.pop();
_currentStep =0;
},
textColor: AppColors.whiteColor,
),
),
],
),
],
),
),
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false,
);
}
}

@ -1,172 +0,0 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class NewEReferral extends StatefulWidget {
NewEReferral();
@override
_NewEReferralState createState() => _NewEReferralState();
}
class _NewEReferralState extends State<NewEReferral> with TickerProviderStateMixin {
late PageController _controller;
int _currentIndex = 0;
int pageSelected = 2;
// CreateEReferralRequestModel createEReferralRequestModel = new CreateEReferralRequestModel();
@override
void initState() {
super.initState();
_controller = new PageController();
}
@override
void dispose() {
super.dispose();
}
changePageViewIndex(pageIndex) {
_controller.jumpToPage(pageIndex);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: double.infinity,
child: Column(
children: [
Container(
width: double.infinity,
padding: EdgeInsets.only(left: 12,right: 12,top: 12),
child: Row(
children: [
Expanded(
child: showProgress(
title: "Requester Info".needTranslation,
status: _currentIndex == 0
? "InProgress".needTranslation
: _currentIndex > 0
? "Completed".needTranslation
: "Locked".needTranslation,
color: _currentIndex == 0 ? AppColors.infoColor : AppColors.successColor,
),
),
Expanded(
child: showProgress(
title:"Patient Info".needTranslation,
status: _currentIndex == 1
? "InProgress".needTranslation
: _currentIndex > 1
? "Completed".needTranslation
: "Locked".needTranslation,
color: _currentIndex == 1
? AppColors.infoColor
: _currentIndex > 1
? AppColors.successColor
: AppColors.greyColor,
),
),
showProgress(
title: "Other Info".needTranslation,
status: _currentIndex == 2 ? "InProgress".needTranslation :"Locked".needTranslation,
color: _currentIndex == 2
? AppColors.infoColor
: _currentIndex > 3
? AppColors.successColor
: AppColors.greyColor,
isNeedBorder: false,
),
],
),
),
Expanded(
child: PageView(
physics: NeverScrollableScrollPhysics(),
controller: _controller,
onPageChanged: (index) {
setState(() {
_currentIndex = index;
});
},
scrollDirection: Axis.horizontal,
children: <Widget>[
// NewEReferralStepOnePage(
// changePageViewIndex: changePageViewIndex,
// createEReferralRequestModel: createEReferralRequestModel,
// ),
// NewEReferralStepTowPage(
// changePageViewIndex: changePageViewIndex,
// createEReferralRequestModel: createEReferralRequestModel,
// ),
// NewEReferralStepThreePage(
// changePageViewIndex: changePageViewIndex,
// createEReferralRequestModel: createEReferralRequestModel,
// ),
],
),
),
],
),
),
);
}
Widget showProgress({required String title, required String status, required Color color, bool isNeedBorder = true}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 26,
height: 26,
// decoration: containerRadius(color, 200),
child: Icon(
Icons.done,
color: Colors.white,
size: 16,
),
),
if (isNeedBorder)
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child:Divider(),
)),
],
),
// mHeight(8),
Text(
title,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
letterSpacing: -0.44,
),
),
// mHeight(2),
Container(
padding: EdgeInsets.all(5),
// decoration: containerRadius(color.withOpacity(0.2), 4),
child: Text(
status,
style: TextStyle(
fontSize: 8,
fontWeight: FontWeight.w600,
letterSpacing: -0.32,
color: color,
),
),
),
],
)
],
);
}
}

@ -0,0 +1,119 @@
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/extensions/string_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/req_models/search_e_referral_req_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/widget/search_e_referral_form.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 'e_referral_form_manager.dart';
import 'e_referral_search_result.dart';
class SearchEReferralPage extends StatefulWidget {
const SearchEReferralPage({
super.key,
});
@override
State<SearchEReferralPage> createState() => _SearchEReferralPageState();
}
class _SearchEReferralPageState extends State<SearchEReferralPage> {
final ReferralFormManager _formManager = ReferralFormManager();
late HmgServicesViewModel hmgServicesVM;
@override
void initState() {
super.initState();
_loadData();
}
void _handleSearch() {
_formManager.validateSearchForm();
if (!_formManager.isSearchFormInValid) {
_searchReferral();
}
}
void _searchReferral() {
SearchEReferralRequestModel searchEReferralReq;
if (_formManager.searchCriteria == 0) {
searchEReferralReq = SearchEReferralRequestModel(
identificationNo: _formManager.searchValue,
patientMobileNumber: _formManager.formData.countryEnum.countryCode + _formManager.searchPhone!,
referralNumber: 0,
);
} else {
searchEReferralReq = SearchEReferralRequestModel(referralNumber: int.parse(_formManager.searchValue!), patientMobileNumber: _formManager.formData.patientPhone, identificationNo: '');
}
hmgServicesVM = context.read<HmgServicesViewModel>();
LoaderBottomSheet.showLoader();
hmgServicesVM.searchEReferral(
requestModel: searchEReferralReq,
onSuccess: (response) async {
LoaderBottomSheet.hideLoader();
await Navigator.of(context).push(
CustomPageRoute(
page: SearchResultPage(),
fullScreenDialog: true,
direction: AxisDirection.down,
),
);
},
);
}
void _loadData() {
WidgetsBinding.instance.addPostFrameCallback((_) {});
}
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: "Search E-Referral".needTranslation,
isClose: true,
bottomChild: Container(
color: Colors.white,
padding: EdgeInsets.all(ResponsiveExtension(20).h),
child: CustomButton(
text: LocaleKeys.search.tr(),
icon: AppAssets.search_icon,
iconColor: Colors.white,
onPressed: () => {
_handleSearch()
},
),
),
child: ChangeNotifierProvider.value(
value: _formManager,
child: Column(
children: [
_buildHeader(),
SearchEReferralFormForm(),
],
)),
);
}
Widget _buildHeader() {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [SizedBox(height: 8), 'Please enter the required information to search for an e-referral'.needTranslation.toText12()],
),
);
}
}

@ -0,0 +1,102 @@
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/enums.dart';
import 'package:hmg_patient_app_new/core/utils/validation_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:provider/provider.dart';
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/send_activation_code_ereferral_req_model.dart';
class OTPService {
static void openOTPScreen({
required BuildContext context,
required ReferralFormManager formManager,
required Function onSuccess,
}) {
_showOTPVerificationSheet(context: context, formManager: formManager, onSuccess: onSuccess);
}
static void _showOTPVerificationSheet({
required BuildContext context,
required ReferralFormManager formManager,
required Function onSuccess,
}) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: false,
useSafeArea: true,
backgroundColor: Colors.transparent,
builder: (bottomSheetContext) => Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom),
child: SingleChildScrollView(
child: GenericBottomSheet(
isEnableCountryDropdown: true,
textController: TextEditingController(),
onChange: (value) {
formManager.updateRequesterPhone(value ?? '');
},
onCountryChange: (value) {
formManager.updateCountryEnum(value);
},
autoFocus: true,
buttons: [
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: CustomButton(
text: LocaleKeys.sendOTPSMS.tr(),
onPressed: () async {
if (ValidationUtils.isValidatePhone(
phoneNumber: formManager.formData.requesterPhone,
onOkPress: () {
Navigator.pop(context);
},
)) {
Navigator.pop(context);
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
LoaderBottomSheet.showLoader();
hmgServicesViewModel.eReferralSendActivationCode(
requestModel: SendActivationCodeForEReferralRequestModel(
patientMobileNumber: int.parse(formManager.formData.requesterPhone),
zipCode: formManager.formData.countryEnum.countryCode,
patientOutSA: formManager.formData.countryEnum.countryCode == '966' ? 0 : 1,
),
onSuccess: (GenericApiModel response) {
LoaderBottomSheet.hideLoader();
hmgServicesViewModel.navigateToOTPScreen(
otpTypeEnum: OTPTypeEnum.sms,
phoneNumber: formManager.formData.requesterPhone,
loginToken: response.data,
onSuccess: () {
Navigator.pop(context);
onSuccess();
});
},
onError: (String errorMessage) {
LoaderBottomSheet.hideLoader();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(errorMessage)));
},
);
}
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedBorderColor,
textColor: AppColors.whiteColor,
icon: AppAssets.message,
),
),
],
),
)),
);
}
}

@ -0,0 +1,319 @@
import 'dart:io';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/utils/validation_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/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'.needTranslation
: '';
}
void _updateInsuranceText() {
final hasInsuranceDocs = _formManager.formData.insuredPatientImages.isNotEmpty;
_insuranceController.text = hasInsuranceDocs
? '${_formManager.formData.insuredPatientImages.length} file(s) selected'.needTranslation
: '';
}
@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'.needTranslation),
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'.needTranslation,
labelText: 'Select Attachment'.needTranslation,
suffix: const Icon(Icons.attachment),
isReadOnly: true,
errorMessage: formManager.errors.medicalReport,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.medicalReport),
),
onTap: () {
ImageOptions.showImageOptionsNew(
context,
false,
(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}'.needTranslation),
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();
}
},
),
Padding(
padding: EdgeInsets.all(5.0),
child:
"Patient is Insured".needTranslation.toText14(
color: Colors.black,
weight: FontWeight.w600,
),
// 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'.needTranslation,
labelText: 'Select Attachment'.needTranslation,
suffix: const Icon(Icons.attachment),
isReadOnly: true,
errorMessage: formManager.errors.insuredDocument,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.insuredDocument),
),
onTap: () {
ImageOptions.showImageOptionsNew(
context,
false,
(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".needTranslation,
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 DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
customBorder: BorderRadius.all(Radius.circular(24.h)) ,
), child: ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.all(16.h),
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
final branch = hospitals[index];
return ListTile(
title: (branch.desciption ?? 'Unknown').toText14(),
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,235 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/utils/validation_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/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/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'.needTranslation),
const SizedBox(height: 12),
_buildIdentificationField(formManager),
_buildPatientNameField(formManager),
// _buildPatientCountryField(context, formManager),
_buildPatientPhoneField(formManager),
const SizedBox(height: 20),
_buildSectionTitle('Where the patient located'.needTranslation),
_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*'.needTranslation,
labelText: 'Identification Number'.needTranslation,
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*'.needTranslation,
labelText: 'Name'.needTranslation,
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 _buildPatientPhoneField(ReferralFormManager formManager) {
return Focus(
focusNode: _phoneFocusNode,
child: TextInputWidget(
labelText: 'Phone Number'.needTranslation,
hintText: "5xxxxxxxx",
controller: _phoneController,
padding: const EdgeInsets.all(8),
keyboardType: TextInputType.number,
onChange: (value) {
formManager.updatePatientPhone(value ?? '');
},
onCountryChange: (value) {
formManager.updatePatientCountryEnum(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".needTranslation,
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 _showCityBottomSheet(BuildContext context, ReferralFormManager formManager) {
showCommonBottomSheetWithoutHeight(
context,
title: "Select City".needTranslation,
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 DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
customBorder: BorderRadius.all(Radius.circular(24.h)) ,
), child: ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.all(16.h),
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
final city = cities[index];
return ListTile(
title: (city.description ?? 'Unknown').toText14(),
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,206 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/utils/validation_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/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/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 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, vertical: 0.0),
child: ListView(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
children: [
// const SizedBox(height: 12),
_buildSectionTitle('Referral requester information'.needTranslation),
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*'.needTranslation,
labelText: 'Requester Name'.needTranslation,
keyboardType: TextInputType.text,
errorMessage: formManager.errors.requesterName,
isAllowLeadingIcon: true,
leadingIcon: AppAssets.user_circle,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterName),
onChange: (value) {
formManager.updateRequesterName(value ?? '');
},
).paddingSymmetrical(0, 8),
);
}
Widget _buildRelationshipField(BuildContext context, ReferralFormManager formManager) {
return DropdownWidget(
labelText: "Relationship".needTranslation,
hintText: formManager.formData.relationship?.textEn ?? "Select Relation".needTranslation,
isEnable: false,
selectedValue: formManager.formData.relationship?.textEn ?? "Select Relation".needTranslation,
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,
keyboardType: TextInputType.text,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
hintText: 'Other Name*'.needTranslation,
labelText: 'Other Name'.needTranslation,
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".needTranslation,
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 DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
customBorder: BorderRadius.all(Radius.circular(24.h)) ,
), child: ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.all(16.h),
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
final relationship = hmgServicesVM.relationTypes[index];
return ListTile(
title:relationship.textEn?.toText14(),
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,199 @@
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/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 {
final VoidCallback? onFormValidated;
const SearchEReferralFormForm({super.key, this.onFormValidated});
@override
State<SearchEReferralFormForm> createState() => SearchEReferralFormFormState();
}
class SearchEReferralFormFormState extends State<SearchEReferralFormForm> {
late TextEditingController _searchController;
late TextEditingController _phoneController;
late FocusNode _searchFocusNode;
late FocusNode _phoneFocusNode;
@override
void initState() {
super.initState();
_searchController = TextEditingController();
_phoneController = TextEditingController();
_searchFocusNode = FocusNode();
_phoneFocusNode = FocusNode();
// Initialize controllers with current values from form manager
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
final formManager = context.read<ReferralFormManager>();
_searchController.text = formManager.searchValue ?? '';
_phoneController.text = formManager.searchPhone ?? '';
// _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(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: [
// const SizedBox(height: 12),
_buildSelectionField(context, formManager),
_buildNameField(formManager),
_buildPhoneField(formManager),
],
),
);
},
);
}
Widget _buildNameField(ReferralFormManager formManager) {
return Focus(
focusNode: _searchFocusNode,
autofocus: true,
child: TextInputWidget(
controller: _searchController,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
hintText: formManager.searchCriteria == 0 ? "Enter Identification Number" : "Enter Referral Number",
labelText: formManager.searchCriteria == 0 ? "Identification Number" : "Referral Number",
keyboardType: TextInputType.number,
errorMessage: formManager.errors.searchValue,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.searchValue),
onChange: (value) {
formManager.updateSearchValue(value ?? '');
//_validateForm(formManager);
},
).paddingSymmetrical(0, 8),
);
}
Widget _buildPhoneField(ReferralFormManager formManager) {
return Focus(
focusNode: _phoneFocusNode,
autofocus: false,
child: TextInputWidget(
autoFocus: false,
labelText: 'Phone Number',
hintText: "5xxxxxxxx",
controller: _phoneController,
padding: const EdgeInsets.all(8),
keyboardType: TextInputType.number,
onChange: (value) {
formManager.updateSearchPhone(value ?? '');
// _validateForm(formManager);
},
// onSubmitted: (value) {
// formManager.updateRequesterPhone(value ?? '');
// _validateForm(formManager);
// },
onCountryChange: (value) {
formManager.updateCountryEnum(value);
// _validateForm(formManager);
},
prefix: '966',
isBorderAllowed: false,
isAllowLeadingIcon: true,
fontSize: 13,
isCountryDropDown: true,
leadingIcon: AppAssets.smart_phone,
errorMessage: formManager.errors.searchPhone,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.searchPhone),
).paddingSymmetrical(0, 8),
);
}
Widget _buildSelectionField(BuildContext context, ReferralFormManager formManager) {
return DropdownWidget(
labelText: "Select the Search Criteria",
hintText: formManager.searchCriteria == 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, formManager);
});
}
void _showCriteriaBottomSheet(BuildContext context, ReferralFormManager formManager) {
final criteriaList = [
{0: 'Identification Number'},
{1: 'Referral Number'},
];
showCommonBottomSheetWithoutHeight(
context,
title: "Select Criteria",
child: ListView.separated(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
final criteria = criteriaList[index];
final criteriaKey = criteria.keys.first;
final criteriaValue = criteria.values.first;
return ListTile(
leading: Radio<int>(
value: criteriaKey,
groupValue: formManager.searchCriteria,
onChanged: (value) {
if (value != null) {
formManager.updateSearchCriteria(value);
_searchController.clear();
_validateForm(formManager);
Navigator.pop(context);
}
},
),
title: Text(criteriaValue),
onTap: () {
formManager.updateSearchCriteria(criteriaKey);
_searchController.clear();
_validateForm(formManager);
Navigator.pop(context);
},
);
},
separatorBuilder: (context, index) => const Divider(),
itemCount: criteriaList.length,
),
isFullScreen: false,
isCloseButtonVisible: true,
);
}
void _validateForm(ReferralFormManager formManager) {
// Trigger validation
formManager.validateSearchForm();
// Notify parent if form validation state changes
widget.onFormValidated?.call();
}
@override
void dispose() {
_searchController.dispose();
_phoneController.dispose();
_searchFocusNode.dispose();
_phoneFocusNode.dispose();
super.dispose();
}
}

@ -23,7 +23,7 @@ class PickupLocation extends StatelessWidget {
children: [
"Select Pickup Direction"
.needTranslation
.toText24(color: AppColors.textColor,isBold: true),
.toText24(color: AppColors.textColor, isBold: true),
SizedBox(
height: 16.h,
),
@ -34,128 +34,147 @@ class PickupLocation extends StatelessWidget {
height: 12.h,
),
Selector<EmergencyServicesViewModel, AmbulanceCallingPlace>(
selector: (context, viewModel) => viewModel.callingPlace,
builder: (context, value, _) {
return Column(
spacing: 12.h,
children: [
RadioGroup<AmbulanceCallingPlace>(
groupValue: value,
onChanged: (value) {
context
.read<EmergencyServicesViewModel>()
.updateCallingPlace(value);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 24.h,
children: [
Row(
children: [
Radio(
value: AmbulanceCallingPlace.TO_HOSPITAL,
groupValue: value,
activeColor: AppColors.primaryRedColor,
fillColor: MaterialStateProperty.all(AppColors.primaryRedColor),
),
"To Hospital"
.needTranslation
.toText14(color: AppColors.textColor, weight: FontWeight.w500)
],
).onPress((){
context
.read<EmergencyServicesViewModel>()
.updateCallingPlace(AmbulanceCallingPlace.TO_HOSPITAL);
}),
Row(
children: [
Radio(
value: AmbulanceCallingPlace.FROM_HOSPITAL,
activeColor: AppColors.primaryRedColor,
fillColor: MaterialStateProperty.all(AppColors.primaryRedColor),
),
"From Hospital"
.needTranslation
.toText14(color: AppColors.textColor, weight: FontWeight.w500)
],
).onPress((){
context
.read<EmergencyServicesViewModel>()
.updateCallingPlace(AmbulanceCallingPlace.FROM_HOSPITAL);
}),
],
selector: (context, viewModel) => viewModel.callingPlace,
builder: (context, value, _) {
return Column(
children: [
SizedBox(height: 12.h),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: Row(
children: [
Radio<AmbulanceCallingPlace>(
value: AmbulanceCallingPlace.TO_HOSPITAL,
groupValue: value,
onChanged: (AmbulanceCallingPlace? newValue) {
if (newValue != null) {
context
.read<EmergencyServicesViewModel>()
.updateCallingPlace(newValue);
}
},
activeColor: AppColors.primaryRedColor,
fillColor: MaterialStateProperty.all(AppColors.primaryRedColor),
),
"To Hospital"
.needTranslation
.toText14(color: AppColors.textColor, weight: FontWeight.w500)
],
).onPress(() {
context
.read<EmergencyServicesViewModel>()
.updateCallingPlace(AmbulanceCallingPlace.TO_HOSPITAL);
}),
),
),
Visibility(
visible: value == AmbulanceCallingPlace.TO_HOSPITAL,
child: Selector<EmergencyServicesViewModel, AmbulanceDirection>(
selector: (context, viewModel) =>
viewModel.ambulanceDirection,
builder: (context, directionValue, _) {
return Column(
spacing: 12.h,
crossAxisAlignment: CrossAxisAlignment.start,
Expanded(
child: Row(
children: [
Radio<AmbulanceCallingPlace>(
value: AmbulanceCallingPlace.FROM_HOSPITAL,
groupValue: value,
onChanged: (AmbulanceCallingPlace? newValue) {
if (newValue != null) {
context
.read<EmergencyServicesViewModel>()
.updateCallingPlace(newValue);
}
},
activeColor: AppColors.primaryRedColor,
fillColor: MaterialStateProperty.all(AppColors.primaryRedColor),
),
"From Hospital"
.needTranslation
.toText14(color: AppColors.textColor, weight: FontWeight.w500)
],
).onPress(() {
context
.read<EmergencyServicesViewModel>()
.updateCallingPlace(AmbulanceCallingPlace.FROM_HOSPITAL);
}),
),
],
),
Visibility(
visible: value == AmbulanceCallingPlace.TO_HOSPITAL,
child: Selector<EmergencyServicesViewModel, AmbulanceDirection>(
selector: (context, viewModel) => viewModel.ambulanceDirection,
builder: (context, directionValue, _) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
"Select Way"
.needTranslation
.toText16(color: AppColors.textColor, weight: FontWeight.w600),
SizedBox(height: 12.h),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
"Select Way"
.needTranslation
.toText16(color: AppColors.textColor, weight: FontWeight.w600),
RadioGroup<AmbulanceDirection>(
groupValue: directionValue,
onChanged: (value) {
Expanded(
child: Row(
children: [
Radio<AmbulanceDirection>(
value: AmbulanceDirection.ONE_WAY,
groupValue: directionValue,
onChanged: (AmbulanceDirection? newValue) {
if (newValue != null) {
context
.read<EmergencyServicesViewModel>()
.updateDirection(newValue);
}
},
activeColor: AppColors.primaryRedColor,
fillColor: MaterialStateProperty.all(AppColors.primaryRedColor),
),
"One Way"
.needTranslation
.toText12(color: AppColors.textColor, fontWeight: FontWeight.w500)
],
).onPress(() {
context
.read<EmergencyServicesViewModel>()
.updateDirection(value);
},
.updateDirection(AmbulanceDirection.ONE_WAY);
}),
),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 24.h,
children: [
Row(
children: [
Radio(
value: AmbulanceDirection.ONE_WAY,
activeColor: AppColors.primaryRedColor,
fillColor: MaterialStateProperty.all(AppColors.primaryRedColor),
),
"One Way"
.needTranslation
.toText12(color: AppColors.textColor, fontWeight: FontWeight.w500)
],
).onPress((){
context
.read<EmergencyServicesViewModel>()
.updateDirection(AmbulanceDirection.ONE_WAY);
}),
Row(
children: [
Radio(
value: AmbulanceDirection.TWO_WAY,
// activeColor: AppColors.primaryRedColor,
fillColor: MaterialStateProperty.all(AppColors.primaryRedColor),
),
"Two Way"
.needTranslation
.toText14(color: AppColors.textColor, weight: FontWeight.w500)
],
).onPress((){
context
.read<EmergencyServicesViewModel>()
.updateDirection(AmbulanceDirection.TWO_WAY);
}),
Radio<AmbulanceDirection>(
value: AmbulanceDirection.TWO_WAY,
groupValue: directionValue,
onChanged: (AmbulanceDirection? newValue) {
if (newValue != null) {
context
.read<EmergencyServicesViewModel>()
.updateDirection(newValue);
}
},
activeColor: AppColors.primaryRedColor,
fillColor: MaterialStateProperty.all(AppColors.primaryRedColor),
),
"Two Way"
.needTranslation
.toText14(color: AppColors.textColor, weight: FontWeight.w500)
],
),
).onPress(() {
context
.read<EmergencyServicesViewModel>()
.updateDirection(AmbulanceDirection.TWO_WAY);
}),
),
],
);
}),
)
],
);
}),
),
],
);
},
),
)
],
);
},
),
SizedBox(
height: 16.h,
),
@ -170,4 +189,4 @@ class PickupLocation extends StatelessWidget {
],
);
}
}
}

@ -1,6 +1,7 @@
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/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/hmg_services_component_model.dart';
import 'package:hmg_patient_app_new/presentation/hmg_services/services_view.dart';
@ -22,7 +23,7 @@ class ServicesPage extends StatelessWidget {
route: AppRoutes.eReferralPage,
),
HmgServicesComponentModel(
12,
5,
"Comprehensive Checkup".needTranslation,
"".needTranslation,
AppAssets.comprehensiveCheckup,
@ -32,7 +33,7 @@ class ServicesPage extends StatelessWidget {
route: AppRoutes.comprehensiveCheckupPage,
),
HmgServicesComponentModel(
12,
3,
"Home Health Care".needTranslation,
"".needTranslation,
AppAssets.emergency_services_icon,
@ -41,6 +42,21 @@ class ServicesPage extends StatelessWidget {
textColor: AppColors.blackColor,
route: AppRoutes.homeHealthCarePage,
),
HmgServicesComponentModel(
11,
"Virtual Tour".needTranslation,
"".needTranslation,
AppAssets.my_address,
true,
bgColor: Colors.orange,
textColor: AppColors.blackColor,
route: null,
onTap:(){
Utils.openWebView(
url: 'https://hmgwebservices.com/vt_mobile/html/index.html',
);
},
)
];
@override
@ -49,7 +65,7 @@ class ServicesPage extends StatelessWidget {
title: "Explore Services".needTranslation,
isLeading: Navigator.canPop(context),
child: Padding(
padding: EdgeInsets.all(24.h),
padding: EdgeInsets.symmetric(horizontal: 24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -60,9 +76,9 @@ class ServicesPage extends StatelessWidget {
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, // 4 icons per row
crossAxisSpacing: 16.w,
mainAxisSpacing: 24.h,
childAspectRatio: 0.75,
crossAxisSpacing: 24.w,
mainAxisSpacing: 0.h,
childAspectRatio: 0.85,
),
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,

@ -11,13 +11,19 @@ class ServiceGridViewItem extends StatelessWidget {
final int index;
final bool isHomePage;
final bool isLocked;
const ServiceGridViewItem(this.hmgServiceComponentModel, this.index, this.isHomePage, {super.key, this.isLocked = false});
final Function? onTap;
const ServiceGridViewItem(this.hmgServiceComponentModel, this.index, this.isHomePage, {super.key, this.isLocked = false, this.onTap});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () => getIt.get<NavigationService>().pushPageRoute(hmgServiceComponentModel.route),
onTap: () {
hmgServiceComponentModel.route != null
? getIt.get<NavigationService>().pushPageRoute(hmgServiceComponentModel.route!)
: hmgServiceComponentModel.onTap != null
? hmgServiceComponentModel.onTap!()
: null;
},
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
@ -33,6 +39,7 @@ class ServiceGridViewItem extends StatelessWidget {
),
child: Utils.buildSvgWithAssets(
icon: hmgServiceComponentModel.icon,
iconColor: hmgServiceComponentModel.iconColor,
height: 21.h,
width: 21.w,
fit: BoxFit.none,

@ -33,7 +33,7 @@ class _LandingNavigationState extends State<LandingNavigation> {
appState.isAuthenticated ? MedicalFilePage() : /* need add feedback page */ FeedbackPage(),
BookAppointmentPage(),
const ToDoPage(),
appState.isAuthenticated ? /* need add news page */ ServicesPage() : const LandingPage(),
ServicesPage(),
],
),
bottomNavigationBar: BottomNavigation(

@ -3,7 +3,7 @@ import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/authentication/register.dart';
import 'package:hmg_patient_app_new/presentation/authentication/register_step2.dart';
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/comprehensive_checkup_page.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_page_home.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/new_e_referral.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_procedures_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
@ -27,7 +27,7 @@ class AppRoutes {
register: (context) => RegisterNew(),
registerStepTwo: (context) => RegisterNewStep2(),
medicalFilePage: (context) => MedicalFilePage(),
eReferralPage: (context) => EReferralPage(),
eReferralPage: (context) => NewReferralPage(),
comprehensiveCheckupPage: (context) => ComprehensiveCheckupPage(),
homeHealthCarePage: (context) => HhcProceduresPage()
};

@ -45,7 +45,7 @@ class AttachmentOptions extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SvgPicture.asset(
"assets/images/$icon",
"assets/images/svg/$icon",
),
title.toText11(isBold: true),
],

@ -19,7 +19,8 @@ class DropdownWidget extends StatelessWidget {
final String? selectionCustomIcon;
final String? leadingIcon;
final Color? labelColor;
final String? errorMessage;
final bool? hasError;
const DropdownWidget(
{Key? key,
required this.labelText,
@ -34,7 +35,10 @@ class DropdownWidget extends StatelessWidget {
this.hasSelectionCustomIcon = false,
this.selectionCustomIcon,
this.leadingIcon,
this.labelColor})
this.labelColor,
this.errorMessage,
this.hasError =false
})
: super(key: key);
@override
@ -42,16 +46,18 @@ class DropdownWidget extends StatelessWidget {
Widget content = Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [_buildLabelText(labelColor), _buildDropdown(context)],
children: [_buildLabelText(labelColor), _buildDropdown(context),],
);
return Container(
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [Container(
padding: padding,
alignment: Alignment.center, // This might need adjustment based on layout
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
borderRadius: isAllowRadius ? 15.h : null,
side: isBorderAllowed ? BorderSide(color: const Color(0xffefefef), width: 1) : null,
side: isBorderAllowed ? BorderSide(color: hasError! ? Colors.red: const Color(0xffefefef), width: 1) : null,
),
child: Row(
// Wrap with a Row
@ -62,9 +68,21 @@ class DropdownWidget extends StatelessWidget {
SizedBox(width: 3.h),
],
Expanded(child: content),
],
),
);
),
if (hasError! && errorMessage != null)
Padding(
padding: EdgeInsets.only(top: 4.h, left: 12.h), // Adjust padding as needed
child: Text(
errorMessage!,
style: TextStyle(
color: Colors.red,
fontSize: 12.f,
),
),
)]);
}
Widget _buildLeadingIcon() {

@ -147,6 +147,7 @@ void cameraImageAndroid(Function(String, File) image) async {
if (base64Encode != null) {
image(base64Encode, _image);
}
}
class _BottomSheet extends StatelessWidget {

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/order_tracking/order_tracking_state.dart';
import 'package:hmg_patient_app_new/widgets/stepper/stepper_widget.dart';
class OrderTrackingWidget extends StatelessWidget {
final double? height;
@ -35,9 +36,9 @@ class OrderTrackingWidget extends StatelessWidget {
return Row(
children: List.generate(4, (index) {
if (index == 0) {
return oneProgressBar(widthOfOneState, AppColors.primaryRedColor, true);
return StepperWidget(widthOfOneState, AppColors.primaryRedColor, true, height);
} else {
return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false);
return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, height);
}
}));
}
@ -46,9 +47,9 @@ class OrderTrackingWidget extends StatelessWidget {
return Row(
children: List.generate(4, (index){
if(index == 0) {
return oneProgressBar(widthOfOneState, AppColors.primaryRedColor, true);
return StepperWidget(widthOfOneState, AppColors.primaryRedColor, true, height);
} else {
return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false);
return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, height);
}
})
);
@ -57,9 +58,9 @@ class OrderTrackingWidget extends StatelessWidget {
return Row(
children: List.generate(4, (index){
if(index == 1 || index == 0) {
return oneProgressBar(widthOfOneState, AppColors.primaryRedColor, index == 1 );
return StepperWidget(widthOfOneState, AppColors.primaryRedColor, index == 1, height );
} else {
return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false);
return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, height);
}
})
);
@ -67,9 +68,9 @@ class OrderTrackingWidget extends StatelessWidget {
return Row(
children: List.generate(4, (index){
if(index == 2 || index == 1 || index == 0) {
return oneProgressBar(widthOfOneState, AppColors.primaryRedColor, index == 2);
return StepperWidget(widthOfOneState, AppColors.primaryRedColor, index == 2, height);
} else {
return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false);
return StepperWidget(widthOfOneState, AppColors.greyLightColor, false, height);
}
})
);
@ -77,7 +78,7 @@ class OrderTrackingWidget extends StatelessWidget {
return Row(
children: List.generate(4, (index){
// if(index == 3) {
return oneProgressBar(widthOfOneState, AppColors.successLightColor, index == 3);
return StepperWidget(widthOfOneState, AppColors.successLightColor, index == 3, height);
// } else {
// return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false);
// }
@ -87,7 +88,7 @@ class OrderTrackingWidget extends StatelessWidget {
return Row(
children: List.generate(4, (index){
// if(index == 3) {
return oneProgressBar(widthOfOneState, AppColors.errorColor, index == 3);
return StepperWidget(widthOfOneState, AppColors.errorColor, index == 3, height);
// } else {
// return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false);
// }
@ -97,7 +98,7 @@ class OrderTrackingWidget extends StatelessWidget {
return Row(
children: List.generate(4, (index){
// if(index == 3) {
return oneProgressBar(widthOfOneState, AppColors.errorColor, index == 3);
return StepperWidget(widthOfOneState, AppColors.errorColor, index == 3, height);
// } else {
// return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false);
// }
@ -106,58 +107,6 @@ class OrderTrackingWidget extends StatelessWidget {
}
}
Widget oneProgressBar(double width, Color color, bool hasThumb) {
return Row(
children: [
AnimatedSize(
duration: const Duration(seconds:1),
child: SizedBox(
height: 28.h,
width: width,
child: Stack(
clipBehavior: Clip.none,
children: [
SizedBox(
height: 28.h,
child: Container(
width: width,
height: height,
decoration: BoxDecoration(
color: color,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(30.h)
),
),
),
Visibility(
visible: hasThumb,
child: Positioned(
top: -6.h, // move thumb above bar center
left: width - 22.h, // move to right end
child: thumb(color),
),
)
],
),
),
),
SizedBox(width: 8.h)
],
);
}
Widget thumb(Color color) {
return Container(
width: 18.h,
height: 18.h,
padding: EdgeInsets.zero,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2.h)
),
);
}
}

@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class StepperWidget extends StatelessWidget {
double width = 80.w;
Color activeColor = AppColors.primaryRedColor;
bool hasThumb = true;
double? height = 4.h;
StepperWidget( this.width, this.activeColor, this.hasThumb, this.height, {super.key});
@override
Widget build(BuildContext context) {
return oneProgressBar(width, activeColor, hasThumb);
}
Widget oneProgressBar(double width, Color color, bool hasThumb) {
return Row(
children: [
AnimatedSize(
duration: const Duration(seconds: 1),
child: SizedBox(
height: 28.h,
width: width,
child: Stack(
clipBehavior: Clip.none,
children: [
SizedBox(
height: height,
child: Container(
width: width,
height: height,
decoration: BoxDecoration(
color: color,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(30.h)
),
),
),
Visibility(
visible: hasThumb,
child: Positioned(
top: -6.h, // move thumb above bar center
left: width - 22.h, // move to right end
child: thumb(color),
),
)
],
),
),
),
SizedBox(width: 8.h)
],
);
}
Widget thumb(Color color) {
return Container(
width: 18.h,
height: 18.h,
padding: EdgeInsets.zero,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2.h)
),
);
}
}
Loading…
Cancel
Save