From 6bc7af00bc3346d2a7b029e184ce2875687c7616 Mon Sep 17 00:00:00 2001 From: faizatflutter Date: Tue, 2 Sep 2025 12:31:05 +0300 Subject: [PATCH 1/4] completed the deviceapi with new architecture --- ios/Podfile.lock | 35 +- lib/core/api/api_client.dart | 532 +++++++++--------- lib/core/app_state.dart | 18 +- lib/core/common_models/generic_api_model.dart | 34 ++ lib/core/dependencies.dart | 62 +- lib/core/exceptions/api_failure.dart | 21 +- lib/core/location_util.dart | 43 +- lib/core/utils/utils.dart | 85 +-- .../authentication/authentication_repo.dart | 62 +- .../authentication_view_model.dart | 45 +- .../models/select_device_by_imei.dart | 124 ++-- lib/main.dart | 15 +- lib/presentation/home/landing_page.dart | 15 +- lib/services/dialog_service.dart | 71 +++ lib/services/error_handler_service.dart | 52 ++ lib/services/navigation_service.dart | 15 + 16 files changed, 735 insertions(+), 494 deletions(-) create mode 100644 lib/core/common_models/generic_api_model.dart create mode 100644 lib/services/dialog_service.dart create mode 100644 lib/services/error_handler_service.dart create mode 100644 lib/services/navigation_service.dart diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5249fe5..c62a44f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -41,11 +41,20 @@ PODS: - file_picker (0.0.1): - DKImagePickerController/PhotoGallery - Flutter + - Firebase/Analytics (11.15.0): + - Firebase/Core + - Firebase/Core (11.15.0): + - Firebase/CoreOnly + - FirebaseAnalytics (~> 11.15.0) - Firebase/CoreOnly (11.15.0): - FirebaseCore (~> 11.15.0) - Firebase/Messaging (11.15.0): - Firebase/CoreOnly - FirebaseMessaging (~> 11.15.0) + - firebase_analytics (11.6.0): + - Firebase/Analytics (= 11.15.0) + - firebase_core + - Flutter - firebase_core (3.15.2): - Firebase/CoreOnly (= 11.15.0) - Flutter @@ -53,6 +62,24 @@ PODS: - Firebase/Messaging (= 11.15.0) - firebase_core - Flutter + - FirebaseAnalytics (11.15.0): + - FirebaseAnalytics/Default (= 11.15.0) + - FirebaseCore (~> 11.15.0) + - FirebaseInstallations (~> 11.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - FirebaseAnalytics/Default (11.15.0): + - FirebaseCore (~> 11.15.0) + - FirebaseInstallations (~> 11.0) + - GoogleAppMeasurement/Default (= 11.15.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) - FirebaseCore (11.15.0): - FirebaseCoreInternal (~> 11.15.0) - GoogleUtilities/Environment (~> 8.1) @@ -371,14 +398,16 @@ SPEC CHECKSUMS: DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49 Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e + firebase_analytics: bf93e20703c95030404d6ddbb1adf05bf5c3885b firebase_core: 99a37263b3c27536063a7b601d9e2a49400a433c firebase_messaging: bf6697c61f31c7cc0f654131212ff04c0115c2c7 + FirebaseAnalytics: 6433dfd311ba78084fc93bdfc145e8cb75740eae FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4 FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843 FirebaseMessaging: 3b26e2cee503815e01c3701236b020aa9b576f09 FLAnimatedImage: bbf914596368867157cc71b38a8ec834b3eeb32b - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4 flutter_ios_voip_kit_karmm: 7ea37381a8841c92d186edf1f4604df5cc437579 flutter_local_notifications: ff50f8405aaa0ccdc7dcfb9022ca192e8ad9688f @@ -387,6 +416,8 @@ SPEC CHECKSUMS: geolocator_apple: 66b711889fd333205763b83c9dcf0a57a28c7afd Google-Maps-iOS-Utils: 66d6de12be1ce6d3742a54661e7a79cb317a9321 google_maps_flutter_ios: e31555a04d1986ab130f2b9f24b6cdc861acc6d3 + GoogleAdsOnDeviceConversion: 2be6297a4f048459e0ae17fad9bfd2844e10cf64 + GoogleAppMeasurement: 700dce7541804bec33db590a5c496b663fbe2539 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 @@ -423,4 +454,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 5df9d8aa8f2c105eacd5ad7a310503d93c68c86b -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index fbf63b2..c3862e2 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -2,21 +2,25 @@ import 'dart:async'; import 'dart:convert'; import 'dart:developer'; import 'dart:io' show Platform; + import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:http/http.dart' as http; +import '../exceptions/api_failure.dart'; + abstract class ApiClient { Future post( String endPoint, { required Map body, - required Function(dynamic response, int statusCode) onSuccess, - required Function(String error, int statusCode) onFailure, + required Function(dynamic response, int statusCode, {int? messageStatus}) onSuccess, + required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure, bool isAllowAny, bool isExternal, bool isRCService, @@ -32,61 +36,69 @@ abstract class ApiClient { bool isRCService, }); - Future simplePost( - String fullUrl, { - required Map body, - required Map headers, - required Function(dynamic response, int statusCode) onSuccess, - required Function(String error, int statusCode) onFailure, - }); - - Future simpleGet( - String fullUrl, { - Function(dynamic response, int statusCode)? onSuccess, - Function(String error, int statusCode)? onFailure, - Map? queryParams, - Map? headers, - }); - - Future simplePut( - String fullUrl, { - Map? body, - Map? headers, - Function(dynamic response, int statusCode)? onSuccess, - Function(String error, int statusCode)? onFailure, - }); - - Future simpleDelete( - String fullUrl, { - Function(dynamic response, int statusCode)? onSuccess, - Function(String error, int statusCode)? onFailure, - Map? queryParams, - Map? headers, - }); - - Future handleUnauthorized(int statusCode, {required String forUrl}); - String getSessionId(String id); - Future generatePackagesToken(); +// Future simplePost( +// String fullUrl, { +// required Map body, +// required Map headers, +// required Function(dynamic response, int statusCode) onSuccess, +// required Function(String error, int statusCode) onFailure, +// }); + +// +// Future simpleGet( +// String fullUrl, { +// Function(dynamic response, int statusCode)? onSuccess, +// Function(String error, int statusCode)? onFailure, +// Map? queryParams, +// Map? headers, +// }); +// +// Future simplePut( +// String fullUrl, { +// Map? body, +// Map? headers, +// Function(dynamic response, int statusCode)? onSuccess, +// Function(String error, int statusCode)? onFailure, +// }); +// +// Future simpleDelete( +// String fullUrl, { +// Function(dynamic response, int statusCode)? onSuccess, +// Function(String error, int statusCode)? onFailure, +// Map? queryParams, +// Map? headers, +// }); + +// Future handleUnauthorized(int statusCode, {required String forUrl}); +// Future generatePackagesToken(); } class ApiClientImp implements ApiClient { final _analytics = getIt(); final LoggerService loggerService; + final AppState appState; + final DialogService dialogService; - ApiClientImp({required this.loggerService}); + ApiClientImp({ + required this.loggerService, + required this.dialogService, + required this.appState, + }); @override - post(String endPoint, - {required Map body, - required Function(dynamic response, int statusCode) onSuccess, - required Function(String error, int statusCode) onFailure, - bool isAllowAny = false, - bool isExternal = false, - bool isRCService = false, - bool bypassConnectionCheck = false}) async { + post( + String endPoint, { + required Map body, + required Function(dynamic response, int statusCode, {int? messageStatus}) onSuccess, + required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure, + bool isAllowAny = false, + bool isExternal = false, + bool isRCService = false, + bool bypassConnectionCheck = false, + }) async { AppState appState = getIt.get(); String url; if (isExternal) { @@ -103,7 +115,7 @@ class ApiClientImp implements ApiClient { Map headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}; if (!isExternal) { String? token = appState.appAuthToken; - String? languageID = (appState.postParamsObject?.languageID == 1 ? 'ar' : 'en') ?? 'ar'; + String? languageID = (appState.postParamsObject?.languageID == 1 ? 'ar' : 'en'); if (endPoint == ApiConsts.sendActivationCode) { languageID = 'en'; } @@ -113,7 +125,6 @@ class ApiClientImp implements ApiClient { if (body.containsKey('LanguageID')) { if (body['LanguageID'] != null) { - //change this line because language issue happened on dental body['LanguageID'] = body['LanguageID'] == 'ar' ? 1 : body['LanguageID'] == 'en' @@ -128,7 +139,6 @@ class ApiClientImp implements ApiClient { : IS_DENTAL_ALLOWED_BACKEND; } - //Todo: I have converted it to string body['DeviceTypeID'] = Platform.isIOS ? "1" @@ -160,40 +170,38 @@ class ApiClientImp implements ApiClient { } } - - body['LanguageID'] = body['LanguageID'] ?? "2"; - body['VersionID'] = body['VersionID'] ?? "18.7"; - body['Channel'] = body['Channel'] ?? "3"; - body['IPAdress'] = body['IPAdress'] ?? "10.20.10.20"; - body['generalid'] = body['generalid'] ?? "Cs2020@2016\$2958"; - body['Latitude'] = body['Latitude'] ?? "0.0"; - body['Longitude'] = body['Longitude'] ?? "0.0"; + body['LanguageID'] = body['LanguageID'] ?? "2"; + body['VersionID'] = body['VersionID'] ?? "18.7"; + body['Channel'] = body['Channel'] ?? "3"; + body['IPAdress'] = body['IPAdress'] ?? "10.20.10.20"; + body['generalid'] = body['generalid'] ?? "Cs2020@2016\$2958"; + body['Latitude'] = body['Latitude'] ?? "0.0"; + body['Longitude'] = body['Longitude'] ?? "0.0"; body['DeviceTypeID'] = body['DeviceTypeID'] ?? - - - (Platform.isIOS ? "1" : await Utils.isGoogleServicesAvailable() ? "2" : "3"); - //"LanguageID":1,"VersionID":18.7,"Channel":3,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","Latitude":0.0,"Longitude":0.0,"DeviceTypeID":2,"PatientType":1} + (Platform.isIOS + ? "1" + : await Utils.isGoogleServicesAvailable() + ? "2" + : "3"); body.removeWhere((key, value) => value == null); - - log("bodi: ${json.encode(body)}"); - log("bodi: ${Uri.parse(url.trim())}"); + log("body: ${json.encode(body)}"); + log("uri: ${Uri.parse(url.trim())}"); if (await Utils.checkConnection(bypassConnectionCheck: bypassConnectionCheck)) { - final response = await http.post(Uri.parse(url.trim()), body: json.encode(body), headers: headers); final int statusCode = response.statusCode; if (statusCode < 200 || statusCode >= 400) { - onFailure('Error While Fetching data', statusCode); + onFailure('Error While Fetching data', statusCode, failureType: ServerFailure("Error While Fetching data")); logApiEndpointError(endPoint, 'Error While Fetching data', statusCode); } else { - var parsed = json.decode(utf8.decode(response.bodyBytes)); + var parsed = json.decode(utf8.decode(response.bodyBytes)); log("parsed: ${parsed.toString()}"); if (isAllowAny) { onSuccess(parsed, statusCode); } else { if (parsed['Response_Message'] != null) { - onSuccess(parsed, statusCode); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus']); } else { if (parsed['ErrorType'] == 4) { //TODO : handle app update @@ -204,19 +212,18 @@ class ApiClientImp implements ApiClient { logApiEndpointError(endPoint, "session logged out", statusCode); } if (isAllowAny) { - onSuccess(parsed, statusCode); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus']); } else if (parsed['IsAuthenticated'] == null) { if (parsed['isSMSSent'] == true) { - onSuccess(parsed, statusCode); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus']); } else if (parsed['MessageStatus'] == 1) { onSuccess(parsed, statusCode); } else if (parsed['Result'] == 'OK') { onSuccess(parsed, statusCode); } else { - - onFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode); + onFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode, + failureType: ServerFailure("Error While Fetching data")); logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode); - } } else if (parsed['MessageStatus'] == 1 || parsed['SMSLoginRequired'] == true) { onSuccess(parsed, statusCode); @@ -226,28 +233,46 @@ class ApiClientImp implements ApiClient { } else { if (parsed['message'] == null && parsed['ErrorEndUserMessage'] == null) { if (parsed['ErrorSearchMsg'] == null) { - onFailure("Server Error found with no available message", statusCode); + onFailure( + "Server Error found with no available message", + statusCode, + failureType: ServerFailure("Error While Fetching data"), + ); logApiEndpointError(endPoint, "Server Error found with no available message", statusCode); } else { - onFailure(parsed['ErrorSearchMsg'], statusCode); + onFailure( + parsed['ErrorSearchMsg'], + statusCode, + failureType: ServerFailure("Error While Fetching data"), + ); logApiEndpointError(endPoint, parsed['ErrorSearchMsg'], statusCode); } } else { - onFailure(parsed['message'] ?? parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode); + onFailure( + parsed['message'] ?? parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], + statusCode, + failureType: ServerFailure("Error While Fetching data"), + ); logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode); } } - } - - else { + } else { if (parsed['SameClinicApptList'] != null) { onSuccess(parsed, statusCode); } else { if (parsed['message'] != null) { - onFailure(parsed['message'] ?? parsed['message'], statusCode); + onFailure( + parsed['message'] ?? parsed['message'], + statusCode, + failureType: ServerFailure("Error While Fetching data"), + ); logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode); } else { - onFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode); + onFailure( + parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], + statusCode, + failureType: ServerFailure("Error While Fetching data"), + ); logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode); } } @@ -256,13 +281,17 @@ class ApiClientImp implements ApiClient { } } } else { - onFailure('Please Check The Internet Connection 1', -1); + onFailure( + 'Please Check The Internet Connection 1', + -1, + failureType: ConnectivityFailure("Error While Fetching data"), + ); _analytics.errorTracking.log("internet_connectivity", error: "no internet available"); } } catch (e) { loggerService.errorLogs(e.toString()); if (e.toString().contains("ClientException")) { - onFailure('Something went wrong, plase try again', -1); + onFailure('Something went wrong, plase try again', -1, failureType: InvalidCredentials('Something went wrong, plase try again')); _analytics.errorTracking.log("internet_connectivity", error: "no internet available"); } else { onFailure(e.toString(), -1); @@ -271,6 +300,7 @@ class ApiClientImp implements ApiClient { } } + @override get(String endPoint, {required Function(dynamic response, int statusCode) onSuccess, required Function(String error, int statusCode) onFailure, @@ -304,203 +334,173 @@ class ApiClientImp implements ApiClient { // print("statusCode :$statusCode"); if (statusCode < 200 || statusCode >= 400) { - onFailure!('Error While Fetching data', statusCode); + onFailure('Error While Fetching data', statusCode); logApiEndpointError(endPoint, 'Error While Fetching data', statusCode); } else { var parsed = json.decode(utf8.decode(response.bodyBytes)); - onSuccess!(parsed, statusCode); - } - } else { - onFailure!('Please Check The Internet Connection', -1); - _analytics.errorTracking.log("internet_connectivity", error: "no internet available"); - } - } - - simplePost( - String fullUrl, { - required Map body, - required Map headers, - required Function(dynamic response, int statusCode) onSuccess, - required Function(String error, int statusCode) onFailure, - }) async { - String url = fullUrl; - // print("URL Query String: $url"); - // print("body: $body"); - - if (await Utils.checkConnection()) { - headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'}); - final response = await http.post( - Uri.parse(url.trim()), - body: json.encode(body), - headers: headers, - ); - final int statusCode = response.statusCode; - // print("statusCode :$statusCode"); - if (await handleUnauthorized(statusCode, forUrl: fullUrl)) - simplePost(fullUrl, onFailure: onFailure, onSuccess: onSuccess, body: body, headers: headers); - - // print(response.body.toString()); - - if (statusCode < 200 || statusCode >= 400) { - onFailure!('Error While Fetching data', statusCode); - logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode); - } else { - onSuccess!(response.body.toString(), statusCode); + onSuccess(parsed, statusCode); } } else { - onFailure!('Please Check The Internet Connection', -1); + onFailure('Please Check The Internet Connection', -1); _analytics.errorTracking.log("internet_connectivity", error: "no internet available"); } } - simpleGet(String fullUrl, - {Function(dynamic response, int statusCode)? onSuccess, - Function(String error, int statusCode)? onFailure, - Map? queryParams, - Map? headers}) async { - headers = headers ?? {}; - String url = fullUrl; - - var haveParams = (queryParams != null); - if (haveParams) { - String queryString = Uri(queryParameters: queryParams).query; - url += '?$queryString'; - // print("URL Query String: $url"); - } - - if (await Utils.checkConnection()) { - headers.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'}); - final response = await http.get( - Uri.parse(url.trim()), - headers: headers, - ); - - final int statusCode = response.statusCode; - // print("statusCode :$statusCode"); - if (await handleUnauthorized(statusCode, forUrl: fullUrl)) - simpleGet(fullUrl, onFailure: onFailure, onSuccess: onSuccess, headers: headers, queryParams: queryParams); - - if (statusCode < 200 || statusCode >= 400) { - onFailure!('Error While Fetching data', statusCode); - logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode); - } else { - onSuccess!(response.body.toString(), statusCode); - } - } else { - onFailure!('Please Check The Internet Connection', -1); - _analytics.errorTracking.log("internet_connectivity", error: "no internet available"); - } - } - - simplePut(String fullUrl, - {Map? body, - Map? headers, - Function(dynamic response, int statusCode)? onSuccess, - Function(String error, int statusCode)? onFailure}) async { - String url = fullUrl; - // print("URL Query String: $url"); - - if (await Utils.checkConnection()) { - headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'}); - final response = await http.put( - Uri.parse(url.trim()), - body: json.encode(body), - headers: headers, - ); - - final int statusCode = response.statusCode; - // print("statusCode :$statusCode"); - if (await handleUnauthorized(statusCode, forUrl: fullUrl)) - simplePut(fullUrl, onFailure: onFailure, onSuccess: onSuccess, headers: headers, body: body); - - if (statusCode < 200 || statusCode >= 400) { - onFailure!('Error While Fetching data', statusCode); - logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode); - } else { - onSuccess!(response.body.toString(), statusCode); - } - } else { - onFailure!('Please Check The Internet Connection', -1); - _analytics.errorTracking.log("internet_connectivity", error: "no internet available"); - } - } - - simpleDelete(String fullUrl, - {Function(dynamic response, int statusCode)? onSuccess, - Function(String error, int statusCode)? onFailure, - Map? queryParams, - Map? headers}) async { - String url = fullUrl; - // print("URL Query String: $url"); - - var haveParams = (queryParams != null); - if (haveParams) { - String queryString = Uri(queryParameters: queryParams).query; - url += '?$queryString'; - // print("URL Query String: $url"); - } - - if (await Utils.checkConnection()) { - headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'}); - final response = await http.delete( - Uri.parse(url.trim()), - headers: headers, - ); - - final int statusCode = response.statusCode; - // print("statusCode :$statusCode"); - if (await handleUnauthorized(statusCode, forUrl: fullUrl)) - simpleDelete(fullUrl, onFailure: onFailure, onSuccess: onSuccess, queryParams: queryParams, headers: headers); - - if (statusCode < 200 || statusCode >= 400) { - onFailure!('Error While Fetching data', statusCode); - logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode); - } else { - onSuccess!(response.body.toString(), statusCode); - } - } else { - onFailure!('Please Check The Internet Connection', -1); - _analytics.errorTracking.log("internet_connectivity", error: "no internet available"); - } - } - - Future handleUnauthorized(int statusCode, {required String forUrl}) async { - if (forUrl.startsWith(EXA_CART_API_BASE_URL) && statusCode == 401) { - final token = await generatePackagesToken(); - ApiConsts.packagesAuthHeader['Authorization'] = 'Bearer $token'; - return (token is String); - } - return false; - } + // @override + // simplePost( + // String fullUrl, { + // required Map body, + // required Map headers, + // required Function(dynamic response, int statusCode) onSuccess, + // required Function(String error, int statusCode) onFailure, + // }) async { + // String url = fullUrl; + // // print("URL Query String: $url"); + // // print("body: $body"); + // + // if (await Utils.checkConnection()) { + // headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'}); + // final response = await http.post( + // Uri.parse(url.trim()), + // body: json.encode(body), + // headers: headers, + // ); + // final int statusCode = response.statusCode; + // // print("statusCode :$statusCode"); + // if (await handleUnauthorized(statusCode, forUrl: fullUrl)) { + // simplePost(fullUrl, onFailure: onFailure, onSuccess: onSuccess, body: body, headers: headers); + // } + // + // // print(response.body.toString()); + // + // if (statusCode < 200 || statusCode >= 400) { + // onFailure!('Error While Fetching data', statusCode); + // logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode); + // } else { + // onSuccess!(response.body.toString(), statusCode); + // } + // } else { + // onFailure!('Please Check The Internet Connection', -1); + // _analytics.errorTracking.log("internet_connectivity", error: "no internet available"); + // } + // } + + // simpleGet(String fullUrl, + // {Function(dynamic response, int statusCode)? onSuccess, + // Function(String error, int statusCode)? onFailure, + // Map? queryParams, + // Map? headers}) async { + // headers = headers ?? {}; + // String url = fullUrl; + // + // var haveParams = (queryParams != null); + // if (haveParams) { + // String queryString = Uri(queryParameters: queryParams).query; + // url += '?$queryString'; + // // print("URL Query String: $url"); + // } + // + // if (await Utils.checkConnection()) { + // headers.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'}); + // final response = await http.get( + // Uri.parse(url.trim()), + // headers: headers, + // ); + // + // final int statusCode = response.statusCode; + // // print("statusCode :$statusCode"); + // if (await handleUnauthorized(statusCode, forUrl: fullUrl)) + // simpleGet(fullUrl, onFailure: onFailure, onSuccess: onSuccess, headers: headers, queryParams: queryParams); + // + // if (statusCode < 200 || statusCode >= 400) { + // onFailure!('Error While Fetching data', statusCode); + // logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode); + // } else { + // onSuccess!(response.body.toString(), statusCode); + // } + // } else { + // onFailure!('Please Check The Internet Connection', -1); + // _analytics.errorTracking.log("internet_connectivity", error: "no internet available"); + // } + // } + + // simplePut(String fullUrl, + // {Map? body, + // Map? headers, + // Function(dynamic response, int statusCode)? onSuccess, + // Function(String error, int statusCode)? onFailure}) async { + // String url = fullUrl; + // // print("URL Query String: $url"); + // + // if (await Utils.checkConnection()) { + // headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'}); + // final response = await http.put( + // Uri.parse(url.trim()), + // body: json.encode(body), + // headers: headers, + // ); + // + // final int statusCode = response.statusCode; + // // print("statusCode :$statusCode"); + // if (await handleUnauthorized(statusCode, forUrl: fullUrl)) + // simplePut(fullUrl, onFailure: onFailure, onSuccess: onSuccess, headers: headers, body: body); + // + // if (statusCode < 200 || statusCode >= 400) { + // onFailure!('Error While Fetching data', statusCode); + // logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode); + // } else { + // onSuccess!(response.body.toString(), statusCode); + // } + // } else { + // onFailure!('Please Check The Internet Connection', -1); + // _analytics.errorTracking.log("internet_connectivity", error: "no internet available"); + // } + // } + // + // simpleDelete(String fullUrl, + // {Function(dynamic response, int statusCode)? onSuccess, + // Function(String error, int statusCode)? onFailure, + // Map? queryParams, + // Map? headers}) async { + // String url = fullUrl; + // // print("URL Query String: $url"); + // + // var haveParams = (queryParams != null); + // if (haveParams) { + // String queryString = Uri(queryParameters: queryParams).query; + // url += '?$queryString'; + // // print("URL Query String: $url"); + // } + // + // if (await Utils.checkConnection()) { + // headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'}); + // final response = await http.delete( + // Uri.parse(url.trim()), + // headers: headers, + // ); + // + // final int statusCode = response.statusCode; + // // print("statusCode :$statusCode"); + // if (await handleUnauthorized(statusCode, forUrl: fullUrl)) + // simpleDelete(fullUrl, onFailure: onFailure, onSuccess: onSuccess, queryParams: queryParams, headers: headers); + // + // if (statusCode < 200 || statusCode >= 400) { + // onFailure!('Error While Fetching data', statusCode); + // logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode); + // } else { + // onSuccess!(response.body.toString(), statusCode); + // } + // } else { + // onFailure!('Please Check The Internet Connection', -1); + // _analytics.errorTracking.log("internet_connectivity", error: "no internet available"); + // } + // } + @override String getSessionId(String id) { return id.replaceAll(RegExp('/[^a-zA-Z]'), ''); } - Future generatePackagesToken() async { - var url = EXA_CART_API_BASE_URL + PACKAGES_TOKEN; - var body = { - "api_client": { - "client_id": "a4ab6be4-424f-4836-b032-46caed88e184", - "client_secret": "3c1a3e07-4a40-4510-9fb0-ee5f0a72752c" - } - }; - String? token; - final completer = Completer(); - simplePost(url, body: body, headers: {}, onSuccess: (dynamic stringResponse, int statusCode) { - if (statusCode == 200) { - var jsonResponse = json.decode(stringResponse); - token = jsonResponse['auth_token']; - completer.complete(); - } - }, onFailure: (String error, int statusCode) { - completer.complete(); - logApiFullUrlError(url, error, statusCode); - }); - await completer.future; - return token!; - } - logApiFullUrlError(String fullUrl, error, code) { final endpoint = Uri.parse(fullUrl).pathSegments.last; logApiEndpointError(endpoint, error, code); diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart index 91e200a..bdf8d25 100644 --- a/lib/core/app_state.dart +++ b/lib/core/app_state.dart @@ -1,15 +1,14 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:hmg_patient_app_new/core/post_params_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/authenticated_user_model.dart'; -import 'package:hmg_patient_app_new/main.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'api_consts.dart' as ApiConsts; - class AppState { - // Simple constructor - let get_it handle the singleton behavior - AppState(); + NavigationService navigationService; + AppState({required this.navigationService}); bool isAuthenticated = true; @@ -40,9 +39,7 @@ class AppState { PostParamsModel? get postParamsObject => _postParams; - Map get postParamsJson => isAuthenticated - ? (_postParams?.toJsonAfterLogin() ?? {}) - : (_postParams?.toJson() ?? {}); + Map get postParamsJson => isAuthenticated ? (_postParams?.toJsonAfterLogin() ?? {}) : (_postParams?.toJson() ?? {}); void setPostParamsModel(PostParamsModel _postParams) { this._postParams = _postParams; @@ -51,12 +48,9 @@ class AppState { double userLat = 0.0; double userLong = 0.0; - bool isArabic() => - EasyLocalization.of(navigatorKey.currentContext!)?.locale.languageCode == - "ar"; + bool isArabic() => EasyLocalization.of(navigationService.navigatorKey.currentContext!)?.locale.languageCode == "ar"; - int getLanguageID(context) => - EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2; + int getLanguageID(context) => EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2; AuthenticatedUser? _authenticatedUser; diff --git a/lib/core/common_models/generic_api_model.dart b/lib/core/common_models/generic_api_model.dart new file mode 100644 index 0000000..1ab11db --- /dev/null +++ b/lib/core/common_models/generic_api_model.dart @@ -0,0 +1,34 @@ +class GenericApiModel { + final int? messageStatus; + final String? errorMessage; + final int? statusCode; + final T? data; + + GenericApiModel({ + this.messageStatus, + this.errorMessage, + this.statusCode, + this.data, + }); + + factory GenericApiModel.fromJson( + Map json, + T Function(Object? json)? fromJsonT, + ) { + return GenericApiModel( + messageStatus: json['messageStatus'] as int?, + errorMessage: json['errorMessage'] as String?, + statusCode: json['statusCode'] as int?, + data: fromJsonT != null ? fromJsonT(json['data']) : json['data'] as T?, + ); + } + + Map toJson(Object Function(T value)? toJsonT) { + return { + 'messageStatus': messageStatus, + 'errorMessage': errorMessage, + 'statusCode': statusCode, + 'data': toJsonT != null && data != null ? toJsonT(data as T) : data, + }; + } +} diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index 440cd26..5aba3e2 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -1,13 +1,18 @@ import 'package:get_it/get_it.dart'; import 'package:hmg_patient_app_new/core/api/api_client.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/location_util.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart'; +import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/common/common_repo.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart'; import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart'; import 'package:hmg_patient_app_new/services/cache_service.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; +import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:logger/web.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -15,25 +20,58 @@ GetIt getIt = GetIt.instance; class AppDependencies { static Future addDependencies() async { - // Services - getIt.registerLazySingleton(() => LoggerServiceImp(logger: Logger(printer: PrettyPrinter( - methodCount: 2, // number of stack trace lines - errorMethodCount: 5, // number of stack trace lines for errors - lineLength: 100, // wrap width - colors: true, // colorful logs - printEmojis: true, // include emojis - ),))); + Logger logger = Logger( + printer: PrettyPrinter( + methodCount: 2, + errorMethodCount: 5, + lineLength: 100, + colors: true, + printEmojis: true, + ), + ); + + // Core Services + getIt.registerLazySingleton(() => LoggerServiceImp(logger: logger)); + getIt.registerLazySingleton(() => NavigationService()); + getIt.registerLazySingleton(() => GAnalytics()); + getIt.registerLazySingleton(() => AppState(navigationService: getIt())); + getIt.registerLazySingleton(() => LocationUtils(isShowConfirmDialog: false, navigationService: getIt())); + getIt.registerLazySingleton(() => DialogServiceImp(navigationService: getIt())); + getIt.registerLazySingleton(() => ErrorHandlerServiceImp( + dialogService: getIt(), + loggerService: getIt(), + navigationService: getIt(), + )); + final sharedPreferences = await SharedPreferences.getInstance(); getIt.registerLazySingleton(() => CacheServiceImp(sharedPreferences: sharedPreferences)); - getIt.registerSingleton(AppState()); - getIt.registerSingleton(GAnalytics()); - getIt.registerLazySingleton(() => ApiClientImp(loggerService: getIt())); + getIt.registerLazySingleton(() => ApiClientImp(loggerService: getIt(), dialogService: getIt(), appState: getIt())); // Repositories getIt.registerLazySingleton(() => CommonRepoImp(loggerService: getIt())); getIt.registerLazySingleton(() => AuthenticationRepoImp(loggerService: getIt(), apiClient: getIt())); - getIt.registerLazySingleton(() => BookAppointmentsRepoImp(loggerService: getIt(), apiClient: getIt())); + getIt.registerLazySingleton( + () => BookAppointmentsRepoImp(loggerService: getIt(), apiClient: getIt())); getIt.registerLazySingleton(() => MyAppointmentsRepoImp(loggerService: getIt(), apiClient: getIt())); + // ViewModels + // Global/shared VMs → LazySingleton + getIt.registerLazySingleton( + () => AuthenticationViewModel( + authenticationRepo: getIt(), + dialogService: getIt(), + appState: getIt(), + errorHandlerService: getIt(), + ), + ); + + // Screen-specific VMs → Factory + // getIt.registerFactory( + // () => BookAppointmentsViewModel( + // bookAppointmentsRepo: getIt(), + // dialogService: getIt(), + // errorHandlerService: getIt(), + // ), + // ); } } diff --git a/lib/core/exceptions/api_failure.dart b/lib/core/exceptions/api_failure.dart index eaa434e..d2a510f 100644 --- a/lib/core/exceptions/api_failure.dart +++ b/lib/core/exceptions/api_failure.dart @@ -1,8 +1,8 @@ - import 'package:equatable/equatable.dart'; abstract class Failure extends Equatable implements Exception { final String message; + const Failure(this.message); } @@ -27,15 +27,30 @@ class LocalStorageFailure extends Failure { List get props => [message]; } +class DataParsingFailure extends Failure { + const DataParsingFailure(super.message); + + @override + List get props => [message]; +} + +class UnknownFailure extends Failure { + const UnknownFailure(super.message); + + @override + List get props => [message]; +} + + class DuplicateUsername extends Failure { - const DuplicateUsername({String? message}) : super(message ?? ''); + const DuplicateUsername(String? message) : super(message ?? ''); @override List get props => [message]; } class InvalidCredentials extends Failure { - const InvalidCredentials({String? message}) : super(message ?? ''); + const InvalidCredentials(String? message) : super(message ?? ''); @override List get props => [message]; diff --git a/lib/core/location_util.dart b/lib/core/location_util.dart index d9716a9..c8e69af 100644 --- a/lib/core/location_util.dart +++ b/lib/core/location_util.dart @@ -1,41 +1,40 @@ import 'dart:io'; +import 'package:geolocator/geolocator.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/consts.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:geolocator/geolocator.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; - +import 'package:hmg_patient_app_new/services/navigation_service.dart'; // import 'package:huawei_location/huawei_location.dart'; import 'package:permission_handler/permission_handler.dart'; -import 'package:provider/provider.dart'; class LocationUtils { + NavigationService navigationService; bool isShowConfirmDialog; bool isShowLocationTimeoutDialog; - BuildContext context; bool isHuawei; final GeolocatorPlatform _geolocatorPlatform = GeolocatorPlatform.instance; - LocationUtils({required this.isShowConfirmDialog, required this.context, this.isHuawei = false, this.isShowLocationTimeoutDialog = true}); + LocationUtils({ + required this.isShowConfirmDialog, + required this.navigationService, + this.isHuawei = false, + this.isShowLocationTimeoutDialog = true, + }); + AppState appState = getIt.get(); void getCurrentLocation({Function(LatLng)? callBack}) async { Geolocator.isLocationServiceEnabled().then((value) async { if (value) { await Geolocator.checkPermission().then((permission) async { if (permission == LocationPermission.always || permission == LocationPermission.whileInUse) { - // Geolocator.getCurrentPosition(locationSettings: LocationSettings(accuracy: LocationAccuracy.medium, timeLimit: Duration(seconds: 5))).then((value) { Geolocator.getLastKnownPosition().then((value) { setLocation(value); if (callBack != null) callBack(LatLng(value?.latitude ?? 24.7101433, value?.longitude ?? 46.6757709)); }).catchError((err) { - print(err); - if (isShowConfirmDialog && isShowLocationTimeoutDialog) { - // showLocationTimeOutDialog(failureCallBack: () { - // Geolocator.openAppSettings(); - // }); - } + if (isShowConfirmDialog && isShowLocationTimeoutDialog) {} }); } @@ -62,15 +61,11 @@ class LocationUtils { } } } - }).catchError((err) { - print(err); - }); + }).catchError((err) {}); } else { if (isShowConfirmDialog) showErrorLocationDialog(false, failureCallBack: () {}); } - }).catchError((err) { - print(err); - }); + }).catchError((err) {}); } Future checkIfGPSIsEnabled() async { @@ -151,8 +146,8 @@ class LocationUtils { Utils.saveNumFromPrefs(SharedPrefsConsts.user_lat, position?.latitude ?? 0.0); Utils.saveNumFromPrefs(SharedPrefsConsts.user_lat, position?.longitude ?? 0.0); - AppState().setUserLat = position?.latitude ?? 0.0; - AppState().setUserLong = position?.longitude ?? 0.0; + appState.setUserLat = position?.latitude ?? 0.0; + appState.setUserLong = position?.longitude ?? 0.0; // projectViewModel.setLatitudeLongitude(position?.latitude ?? 0.0, position?.longitude ?? 0.0); } @@ -161,8 +156,8 @@ class LocationUtils { Utils.saveNumFromPrefs(SharedPrefsConsts.user_lat, 0.0); Utils.saveNumFromPrefs(SharedPrefsConsts.user_lat, 0.0); - AppState().setUserLat = 0.0; - AppState().setUserLong = 0.0; + appState.setUserLat = 0.0; + appState.setUserLong = 0.0; } Future requestPermissions() async { diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index 4f7b398..14e91f8 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -1,24 +1,28 @@ import 'dart:convert'; -import 'package:crypto/crypto.dart' as crypto; 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_svg/flutter_svg.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:google_api_availability/google_api_availability.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/main.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/dialogs/confirm_dialog.dart'; import 'package:hmg_patient_app_new/widgets/loading_dialog.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:fluttertoast/fluttertoast.dart'; import 'package:lottie/lottie.dart'; import 'package:shared_preferences/shared_preferences.dart'; class Utils { + static AppState appState = getIt.get(); + static NavigationService navigationService = getIt.get(); + static bool _isLoadingVisible = false; static bool get isLoading => _isLoadingVisible; @@ -47,14 +51,16 @@ class Utils { } static String getFreeSlotsTimeText(String startTime, {bool isAddHours = false}) { - // return DateFormat('hh:mm a', AppState().isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime)!.add( + // return DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime)!.add( // Duration( // hours: isAddHours ? 3 : 0, // ), // )); return !isAddHours - ? DateFormat('hh:mm a', AppState().isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.toLocal()) - : DateFormat('hh:mm a', AppState().isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.add( + ? DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US") + .format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.toLocal()) + : DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US") + .format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.add( Duration( hours: isAddHours ? 3 : 0, ), @@ -82,12 +88,9 @@ class Utils { } static String getMonthDayYearDateFormatted(DateTime dateTime) { - if (dateTime != null) - return AppState().isArabic() - ? getMonthArabic(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString() - : getMonth(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString(); - else - return ""; + return appState.isArabic() + ? getMonthArabic(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString() + : getMonth(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString(); } /// get month by @@ -200,7 +203,7 @@ class Utils { static void showLoadingDialog() { _isLoadingVisible = true; showDialog( - context: navigatorKey.currentContext!, + context: navigationService.navigatorKey.currentContext!, barrierColor: Colors.black.withOpacity(0.5), builder: (BuildContext context) => LoadingDialog(), ) @@ -217,7 +220,7 @@ class Utils { try { if (_isLoadingVisible) { _isLoadingVisible = false; - Navigator.of(navigatorKey.currentContext!).pop(); + Navigator.of(navigationService.navigatorKey.currentContext!).pop(); } _isLoadingVisible = false; } catch (e) {} @@ -242,9 +245,6 @@ class Utils { static bool isSAUDIIDValid(String id, type) { if (type == 1) { - if (id == null) { - return false; - } try { id = id.toString(); id = id.trim(); @@ -304,13 +304,15 @@ class Utils { } static String removeHtmlTags(String htmlString) { - if (htmlString == null || htmlString.isEmpty) { + if (htmlString.isEmpty) { return ''; } // Replace HTML line breaks with newlines - var withLineBreaks = - htmlString.replaceAll(RegExp(r'', multiLine: true), '\n').replaceAll(RegExp(r'<\/p>', multiLine: true), '\n').replaceAll(RegExp(r'', multiLine: true), '\n'); + var withLineBreaks = htmlString + .replaceAll(RegExp(r'', multiLine: true), '\n') + .replaceAll(RegExp(r'<\/p>', multiLine: true), '\n') + .replaceAll(RegExp(r'', multiLine: true), '\n'); // Remove all other HTML tags var withoutTags = withLineBreaks.replaceAll(RegExp(r'<[^>]*>'), ''); @@ -376,7 +378,20 @@ class Utils { final year = parts[0]; // Map month number to short month name (Hijri months) - const hijriMonthNames = ['Muharram', 'Safar', 'Rabi I', 'Rabi II', 'Jumada I', 'Jumada II', 'Rajab', 'Sha\'ban', 'Ramadan', 'Shawwal', 'Dhu al-Qi\'dah', 'Dhu al-Hijjah']; + const hijriMonthNames = [ + 'Muharram', + 'Safar', + 'Rabi I', + 'Rabi II', + 'Jumada I', + 'Jumada II', + 'Rajab', + 'Sha\'ban', + 'Ramadan', + 'Shawwal', + 'Dhu al-Qi\'dah', + 'Dhu al-Hijjah' + ]; final monthIndex = int.tryParse(parts[1]) ?? 1; final month = hijriMonthNames[monthIndex - 1]; @@ -458,13 +473,8 @@ class Utils { ); } - - - static Future isGoogleServicesAvailable() async { - GooglePlayServicesAvailability availability = await GoogleApiAvailability - .instance - .checkGooglePlayServicesAvailability(); + GooglePlayServicesAvailability availability = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability(); String status = availability.toString().split('.').last; if (status == "success") { return true; @@ -472,26 +482,17 @@ class Utils { return false; } - - - - - static Future checkConnection( - {bool bypassConnectionCheck = false}) async { + static Future checkConnection({bool bypassConnectionCheck = false}) async { if (bypassConnectionCheck) return true; - List connectivityResult = - await (Connectivity().checkConnectivity()); - if (connectivityResult.contains(ConnectivityResult.mobile) || - connectivityResult.contains(ConnectivityResult.wifi)) { + List connectivityResult = await (Connectivity().checkConnectivity()); + if (connectivityResult.contains(ConnectivityResult.mobile) || connectivityResult.contains(ConnectivityResult.wifi)) { return true; } else { return false; } } - static String generateMd5Hash(String input) { return crypto.md5.convert(utf8.encode(input)).toString(); } - } diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart index e544638..04e3a9e 100644 --- a/lib/features/authentication/authentication_repo.dart +++ b/lib/features/authentication/authentication_repo.dart @@ -1,16 +1,15 @@ import 'dart:async'; -import 'dart:developer'; import 'package:dartz/dartz.dart'; 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/exceptions/api_exception.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/authentication/models/select_device_by_imei.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; abstract class AuthenticationRepo { - Future> selectDeviceByImei({required String firebaseToken}); + Future>> selectDeviceByImei({required String firebaseToken}); } class AuthenticationRepoImp implements AuthenticationRepo { @@ -20,38 +19,43 @@ class AuthenticationRepoImp implements AuthenticationRepo { AuthenticationRepoImp({required this.loggerService, required this.apiClient}); @override - Future> selectDeviceByImei({ + Future>> selectDeviceByImei({ required String firebaseToken, }) async { final mapDevice = {"IMEI": firebaseToken}; try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + ApiConsts.selectDeviceImei, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus}) { + try { + final list = response['Patient_SELECTDeviceIMEIbyIMEIList'] as List?; + if (list == null || list.isEmpty) { + throw Exception("Device list is empty"); + } - final completer = Completer>(); - await apiClient.post( - ApiConsts.selectDeviceImei, - body: mapDevice, - onSuccess: (response, statusCode) { - try { - final SelectDeviceByImeiRespModelElement model = - SelectDeviceByImeiRespModelElement.fromJson(response['Patient_SELECTDeviceIMEIbyIMEIList'][0]); - completer.complete(Right(model)); - } catch (e) { - completer.complete(Left(ServerFailure(e.toString()))); - } - }, - onFailure: (error, statusCode) { - loggerService.logInfo(("$error - $statusCode").toString()); - completer.complete(Left(ServerFailure(error))); - }, - ); - - return await completer.future; - } on APIException catch (e) { - loggerService.errorLogs(e.toString()); - return Left(ServerFailure(e.message)); + final model = SelectDeviceByImeiRespModelElement.fromJson(list[0] as Map); + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: model, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); } catch (e) { - loggerService.errorLogs(e.toString()); - return Left(ServerFailure(e.toString())); + return Left(UnknownFailure(e.toString())); } } } diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index 8269c72..eaf3c95 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -1,46 +1,37 @@ -import 'dart:developer'; -import 'dart:io'; - import 'package:flutter/material.dart'; -import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; -import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart'; -import 'package:hmg_patient_app_new/features/authentication/models/check_activation_code_request_register.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; +import 'package:hmg_patient_app_new/services/error_handler_service.dart'; class AuthenticationViewModel extends ChangeNotifier { AuthenticationRepo authenticationRepo; AppState appState; + ErrorHandlerService errorHandlerService; + DialogService dialogService; AuthenticationViewModel({ required this.appState, required this.authenticationRepo, + required this.errorHandlerService, + required this.dialogService, }); final TextEditingController nationalIdController = TextEditingController(); final TextEditingController phoneNumberController = TextEditingController(); - Future selectDeviceImei({ - Function(dynamic)? onSuccess, - Function(String)? onError - }) async { - final String firebaseToken = - "cIkkB7h7Q7uoFkC4Qv82xG:APA91bEb53Z9XzqymCIctaLxCoMX6bm9fuKlWILQ59uUqfwhCoD42AOP1-jWGB1WYd9BVN5PT2pUUFxrT07vcNg1KH9OH39mrPgCl0m21XVIgWrzNnCkufg"; - - final resultEither = - await authenticationRepo.selectDeviceByImei(firebaseToken: firebaseToken); - - resultEither.fold( - (failure) { - notifyListeners(); - if (onError != null) onError(failure.message); - }, - (data) { - - log("resultEither: ${data.toString()} "); - - notifyListeners(); - if (onSuccess != null) onSuccess(data); + Future selectDeviceImei({Function(dynamic)? onSuccess, Function(String)? onError}) async { + String firebaseToken = + "dOGRRszQQMGe_9wA5Hx3kO:APA91bFV5IcIJXvcCXXk0tc2ddtZgWwCPq7sGSuPr-YW7iiJpQZKgFGN9GAzCVOWL8MfheaP1slE8MdxB7lczdPBGdONQ7WbMmhgHcsUCUktq-hsapGXXqc"; + final result = await authenticationRepo.selectDeviceByImei(firebaseToken: firebaseToken); + result.fold( + (failure) async => await errorHandlerService.handleError(failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + dialogService.showErrorDialog(apiResponse.errorMessage!); + } else if (apiResponse.messageStatus == 1) { + // move to next api call + } }, ); } diff --git a/lib/features/authentication/models/select_device_by_imei.dart b/lib/features/authentication/models/select_device_by_imei.dart index 2ab669e..398f791 100644 --- a/lib/features/authentication/models/select_device_by_imei.dart +++ b/lib/features/authentication/models/select_device_by_imei.dart @@ -1,77 +1,73 @@ -// To parse this JSON data, do -// -// final selectDeviceByImeiRespModel = selectDeviceByImeiRespModelFromJson(jsonString); - import 'dart:convert'; -Map selectDeviceByImeiRespModelFromJson(String str) => Map.from(json.decode(str)).map((k, v) => MapEntry(k, v)); +Map selectDeviceByImeiRespModelFromJson(String str) => Map.from(json.decode(str)); -String selectDeviceByImeiRespModelToJson(Map data) => json.encode(Map.from(data).map((k, v) => MapEntry(k, v))); +String selectDeviceByImeiRespModelToJson(Map data) => json.encode(Map.from(data)); class SelectDeviceByImeiRespModelElement { - int id; - String imei; - int logInType; - int patientId; - bool outSa; - String mobile; - String identificationNo; - String name; - String nameN; - String createdOn; - String editedOn; - bool biometricEnabled; - int patientType; - int preferredLanguage; + int? id; + String? imei; + int? logInType; + int? patientId; + bool? outSa; + String? mobile; + String? identificationNo; + String? name; + String? nameN; + String? createdOn; + String? editedOn; + bool? biometricEnabled; + int? patientType; + int? preferredLanguage; SelectDeviceByImeiRespModelElement({ - required this.id, - required this.imei, - required this.logInType, - required this.patientId, - required this.outSa, - required this.mobile, - required this.identificationNo, - required this.name, - required this.nameN, - required this.createdOn, - required this.editedOn, - required this.biometricEnabled, - required this.patientType, - required this.preferredLanguage, + this.id, + this.imei, + this.logInType, + this.patientId, + this.outSa, + this.mobile, + this.identificationNo, + this.name, + this.nameN, + this.createdOn, + this.editedOn, + this.biometricEnabled, + this.patientType, + this.preferredLanguage, }); factory SelectDeviceByImeiRespModelElement.fromJson(Map json) => SelectDeviceByImeiRespModelElement( - id: json["ID"], - imei: json["IMEI"], - logInType: json["LogInType"], - patientId: json["PatientID"], - outSa: json["OutSA"], - mobile: json["Mobile"], - identificationNo: json["IdentificationNo"], - name: json["Name"], - nameN: json["NameN"], - createdOn: json["CreatedOn"], - editedOn: json["EditedOn"], - biometricEnabled: json["BiometricEnabled"], - patientType: json["PatientType"], - preferredLanguage: json["PreferredLanguage"], - ); + id: json["ID"] as int?, + imei: json["IMEI"] as String?, + logInType: json["LogInType"] as int?, + patientId: json["PatientID"] as int?, + outSa: json["OutSA"] as bool?, + mobile: json["Mobile"] as String?, + identificationNo: json["IdentificationNo"] as String?, + name: json["Name"] as String?, + nameN: json["NameN"] as String?, + createdOn: json["CreatedOn"] as String?, + editedOn: json["EditedOn"] as String?, + biometricEnabled: json["BiometricEnabled"] as bool?, + patientType: json["PatientType"] as int?, + preferredLanguage: json["PreferredLanguage"] as int?, + ); Map toJson() => { - "ID": id, - "IMEI": imei, - "LogInType": logInType, - "PatientID": patientId, - "OutSA": outSa, - "Mobile": mobile, - "IdentificationNo": identificationNo, - "Name": name, - "NameN": nameN, - "CreatedOn": createdOn, - "EditedOn": editedOn, - "BiometricEnabled": biometricEnabled, - "PatientType": patientType, - "PreferredLanguage": preferredLanguage, - }; + "ID": id, + "IMEI": imei, + "LogInType": logInType, + "PatientID": patientId, + "OutSA": outSa, + "Mobile": mobile, + "IdentificationNo": identificationNo, + "Name": name, + "NameN": nameN, + "CreatedOn": createdOn, + "EditedOn": editedOn, + "BiometricEnabled": biometricEnabled, + "PatientType": patientType, + "PreferredLanguage": preferredLanguage, + }; } diff --git a/lib/main.dart b/lib/main.dart index 3d25c0a..bb4332b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,16 +10,14 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_view_ import 'package:hmg_patient_app_new/providers/bottom_navigation_provider.dart'; import 'package:hmg_patient_app_new/routes/app_routes.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/app_theme.dart'; import 'package:provider/provider.dart'; import 'package:provider/single_child_widget.dart'; + import 'core/utils/size_utils.dart'; import 'firebase_options.dart'; -var globalMessengerKey = GlobalKey(); -final navigatorKey = GlobalKey(); - - @pragma('vm:entry-point') Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); @@ -57,7 +55,12 @@ void main() async { create: (_) => BottomNavigationProvider(), ), ChangeNotifierProvider( - create: (_) => AuthenticationViewModel(authenticationRepo: getIt(), appState: getIt()), + create: (_) => AuthenticationViewModel( + authenticationRepo: getIt(), + appState: getIt(), + dialogService: getIt(), + errorHandlerService: getIt(), + ), ), ], child: MyApp()), ), @@ -94,7 +97,7 @@ class MyApp extends StatelessWidget { initialRoute: AppRoutes.initialRoute, routes: AppRoutes.routes, theme: AppTheme.getTheme(EasyLocalization.of(context)?.locale.languageCode == "ar"), - navigatorKey: navigatorKey, + navigatorKey: getIt.get().navigatorKey, ); }, ); diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index feb7f69..22355cc 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -3,11 +3,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart'; @@ -25,9 +27,10 @@ class LandingPage extends StatefulWidget { } class _LandingPageState extends State { - @override Widget build(BuildContext context) { + AppState appState = getIt.get(); + final AuthenticationViewModel authenticationViewModel = context.read(); return Consumer(builder: (context, navigationProvider, child) { return Scaffold( backgroundColor: AppColors.bgScaffoldColor, @@ -43,10 +46,8 @@ class _LandingPageState extends State { children: [ CustomButton( text: LocaleKeys.loginOrRegister.tr(context: context), - onPressed: () { - Navigator.of(context).pushReplacement( - MaterialPageRoute(builder: (BuildContext context) => LandingPage()), - ); + onPressed: () async { + await authenticationViewModel.selectDeviceImei(); }, backgroundColor: Color(0xffFEE9EA), borderColor: Color(0xffFEE9EA), @@ -66,7 +67,7 @@ class _LandingPageState extends State { ), ), SizedBox(height: 16.h), - AppState().isAuthenticated + appState.isAuthenticated ? Column( children: [ Container( @@ -257,7 +258,7 @@ class _LandingPageState extends State { ), ), SizedBox(height: 16.h), - AppState().isAuthenticated + appState.isAuthenticated ? Column( children: [ Row( diff --git a/lib/services/dialog_service.dart b/lib/services/dialog_service.dart new file mode 100644 index 0000000..e931568 --- /dev/null +++ b/lib/services/dialog_service.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; + +abstract class DialogService { + Future showErrorDialog(String message); +} + +class DialogServiceImp implements DialogService { + final NavigationService navigationService; + + DialogServiceImp({required this.navigationService}); + + @override + Future showErrorDialog(String message) async { + final context = navigationService.navigatorKey.currentContext; + if (context == null) return; + + await showModalBottomSheet( + context: context, + isScrollControlled: false, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(16)), + ), + builder: (_) => _ErrorBottomSheet(message: message), + ); + } +} + +class _ErrorBottomSheet extends StatelessWidget { + final String message; + + const _ErrorBottomSheet({required this.message}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.error_outline, color: Colors.red, size: 40), + const SizedBox(height: 12), + Text( + "Error", + style: Theme.of(context).textTheme.titleLarge?.copyWith( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 8), + Text( + message, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () => Navigator.of(context).pop(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text("OK"), + ), + ], + ), + ); + } +} diff --git a/lib/services/error_handler_service.dart b/lib/services/error_handler_service.dart new file mode 100644 index 0000000..d8d473a --- /dev/null +++ b/lib/services/error_handler_service.dart @@ -0,0 +1,52 @@ +import 'dart:io'; + +import 'package:hmg_patient_app_new/core/exceptions/api_exception.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; + +abstract class ErrorHandlerService { + Future handleError(Failure failure); +} + +class ErrorHandlerServiceImp implements ErrorHandlerService { + final DialogService dialogService; + final LoggerService loggerService; + final NavigationService navigationService; + + ErrorHandlerServiceImp({ + required this.dialogService, + required this.loggerService, + required this.navigationService, + }); + + @override + Future handleError(Failure failure) async { + if (failure is APIException) { + loggerService.errorLogs("API Exception: ${failure.message}"); + } else if (failure is ServerFailure) { + loggerService.errorLogs("Server Failure: ${failure.message}"); + await _showDialog(failure); + } else if (failure is DataParsingFailure) { + loggerService.errorLogs("Data Parsing Failure: ${failure.message}"); + await _showDialog(failure, title: "Data Error"); + } else if (failure is HttpException) { + loggerService.errorLogs("Http Exception: ${failure.message}"); + await _showDialog(failure, title: "Network Error"); + } else if (failure is UnknownFailure) { + loggerService.errorLogs("Unknown Failure: ${failure.message}"); + await _showDialog(failure, title: "Unknown Error"); + } else if (failure is InvalidCredentials) { + loggerService.errorLogs("Invalid Credentials : ${failure.message}"); + await _showDialog(failure, title: "Unknown Error"); + } else { + loggerService.errorLogs("Unhandled failure type: $failure"); + await _showDialog(failure, title: "Error"); + } + } + + Future _showDialog(Failure failure, {String title = "Error"}) async { + await dialogService.showErrorDialog(failure.message); + } +} diff --git a/lib/services/navigation_service.dart b/lib/services/navigation_service.dart new file mode 100644 index 0000000..d814cf5 --- /dev/null +++ b/lib/services/navigation_service.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class NavigationService { + final GlobalKey navigatorKey = GlobalKey(); + + BuildContext? get context => navigatorKey.currentContext; + + Future push(Route route) { + return navigatorKey.currentState!.push(route); + } + + void pop([T? result]) { + navigatorKey.currentState!.pop(result); + } +} From 4ecbcb9332fe13a529a14f042733f52bd07436b8 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Tue, 2 Sep 2025 14:45:56 +0300 Subject: [PATCH 2/4] Homepage & medical implementation contd. --- assets/images/png/Apple_Pay.png | Bin 0 -> 6592 bytes assets/images/png/Mada.png | Bin 0 -> 10340 bytes assets/images/png/Mastercard.png | Bin 0 -> 14639 bytes assets/images/png/male_img.png | Bin 0 -> 19600 bytes assets/images/png/tamara_en.png | Bin 0 -> 46326 bytes assets/images/png/visa.png | Bin 0 -> 16450 bytes lib/core/app_assets.dart | 19 ++- lib/core/utils/utils.dart | 15 +- lib/presentation/home/landing_page.dart | 41 ++--- .../home/widgets/habib_wallet_card.dart | 127 ++++++++++++++ .../medical_file/medical_file_page.dart | 158 +++++++++++++++++- lib/theme/colors.dart | 1 + lib/widgets/buttons/custom_button.dart | 4 +- 13 files changed, 330 insertions(+), 35 deletions(-) create mode 100644 assets/images/png/Apple_Pay.png create mode 100644 assets/images/png/Mada.png create mode 100644 assets/images/png/Mastercard.png create mode 100644 assets/images/png/male_img.png create mode 100644 assets/images/png/tamara_en.png create mode 100644 assets/images/png/visa.png create mode 100644 lib/presentation/home/widgets/habib_wallet_card.dart diff --git a/assets/images/png/Apple_Pay.png b/assets/images/png/Apple_Pay.png new file mode 100644 index 0000000000000000000000000000000000000000..1dc75042f37de9d6b7629dc7e3f33e7c5d5671b4 GIT binary patch literal 6592 zcmV;x89(NUP)Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR92ET97b z1ONa40RR91djJ3c0J_IuqW}OIHc3Q5RCodHT?^P2RkhXtQ8BZB}_$yyo*X| z26-zl2g`eESF@9PEX+%3ob>4Ps70AKo&-$IFw&!`C2Ho4Kul8v0u?0V@~csxnCHCj z4->=u-}lX4Yi@gH)_R`z|GsZ#t-aQM_sp8T_w1Q}z4fXsy-~fhfC<10z&k)+z{D6Z z28;nO80ds3jsqS6{ssJB1cUM0#27FJjDbQKSRdX;1G9l>k_!JGRA`ju+!!zhYR|xq zVBk?8oS2$@->UX(83D$CF;Ex-r@-@apjm_A>dWO2S7L} zwfg>CZP~Jl8v_+!pmT}qFb=VQQ5gR-LVe%VH}oVRUKCaVyjYkR1GzC!)`WE_7?LI{ zlKL?JlvtJ__KiG>|DTAZ4Oj@w16~5A15W{q0TW}O_6&R$3`mB7!4Q!rSND>$$2tV{ z@%fX>NoE390DAx?#y~9@=!07Gimo|XCsly4vLvi%`#cZs*(3I;cUXbL!K&ap15*%S z^ki`Ty|3?;bBM332`gx$zoL%k0$m;IYAVJ+sTnvN(FX^<^z)HY*L-d8WlvZ^yS;)c z9lEw!_RScm9s|=*rQm>X~02?M&hoo-aErLBoUqu& zTx_~Si~8FxW59a`u0({AgvCcjPWQf6x&W)5u-MMCfF3$}%&O`Pl;P2wT?LK6H;R4^ zxL06P2UYf5n8=ez7Z`JRzHyR59BlcjpqqhX9du2_81RmPml2Um59?{@Red<2?O~<) zcnlL3UxLV?`XvCL&*W=+T-r#Ya|+5dF$PM<09Qx1I?$J3|2Y6(ja+r{Az?M*n9pdQ z2Jmedt=dYmkL|hzU}6lEih*VcYYu900l<@`F5+v#3iJO-{CE+NB&TM&*M|$X@5VqG z7`P8Xaw7Iw;7;IMz%ZbWQig=ZcG&>9641O=Bb>arG^H6^KY(Tjn#*=3m`8zA0p2oq73!W0JO*f!RHOK~ zO8y>TcfdC(Yr^7szvqBPe7kIWz_*65c*?^7S0P^ma6;7OV3X*yq0Acq4(A#TSGjHf zH9BwOQ&P56mx=2}AUvGJ&qAfF35%p13`pA`(C0d@53>*xV?e?HZ&|+%1P4-X_7= z0ZxXt5+hofI}26SngpfJYojPQ-5*5S8O+6%y7N3686QDRTWGlyxM)N56Ox%IX;A zyzfK6@jzP1;=%;+7Im>j$nC=ANZO@PpIK8zhmYad@;;DNBbK?`dEjN=4vgiqp$ZA> z&;-X-8vp%>BZtudpK{3iIQ$EIegWtNB$2)_*4_ZSHi_dDY>!468s53&scR$u%zz|T zQ)ty~?guYD8k|HfuP zU!g2e6GWM8ZUrxCb?KXNAGf^<32ShI<4b$
Tkad4vCmt&42n9BghrjgTOq^-*k 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 literal 0 HcmV?d00001 diff --git a/assets/images/png/Mada.png b/assets/images/png/Mada.png new file mode 100644 index 0000000000000000000000000000000000000000..905a5ba893f3e650656028f91f2f7833b5408ede GIT binary patch literal 10340 zcma)CRZtvEvt8WX-QC^Y-C=Q;#ogWAHF&V#?(Q0bLvVKjED$6RZoY^A{XX=}>8Yuy zI{nbob-Lr!Rpn3+2@wGR0E&XVv?c%m!Tb+f!^8etpN)5||7{Q+nsSnWhI!(Pe-6Jj zP{Bq;1;F?Z!vi2VFvU-$MXvJOyz8fNoPkT0+|w z;wA{e*J?b-R;T~@jnud-LSSK4vy+=eHGer2g%=}} zzjHhH(l;!MC4(y%yL0^gwHvZ&8FM1TzMo4b-4GcU4tXR2ab*r)VBx zNUC=}X(`GG2Y=gh`b+!@RGNM5WjMi8SCNh~JJY1Ci6fUd=7Ap%sX)c`D`~u4@Bzmq zwfP8(L+=!_iD0QAR7uVq{lPCigg-bm<5VaX^KX0L*>Wc;#82=uNT-zL@kX&e{vPT< zMqz<>hjL)@AWkAx6M2=3F0FiYMUHU@!#b71VQ1#JA zdefrOb-9O$^wy6tqebF#Wo0FjvBv&0Q{yd$oK!fHzD`>hy5v+d!;!VwkqfLjEoPF5 zP_iK>CA;6TW_MjQ+FxiY8_21rg>8N!DKn0f6iSWQ7&4hdzHg(b50Hj(IOSHrVZmFQ zGVhEUUEJ^@EPWm!6u?vSgCem^!!~k{*ot=qm+n{un;fs^`Pt19`TvBUNoxbD4p@|a zw#?>9kb&Z^d+B!9pFm@I0`ZyLHORoMniJVd>-xOD(sQE@qQzZcw|3VM2F{dtG_!x-ibCqI{U*x_sdj zgGlj?BN@9|HE%mB{hVkdLcx8uf!5nZgZQfkdoMbVeB5Y_YvGw(6QXhJV=m%L{zMd{ zDQu7AiK|Q+-GS{)KefbX!nws>t~SM|9(<*wu-9F!)g#+6(VlAMp{eybB1Gcnv7%zF zbydbwNBoYkRKejg^fLTwr?)l-PqY=!tDE-nPqs`6Y$Lq# zI;rIIcB_f>uz*)vlPBAsI^w)^l9$AY={V}TJ_yF_k&&6~ltun~^VFGu7A(cbt%S}$ zDN<|xhHmc6(AiWXY(U+oL5;nOtL|SoIq>u}Hds{Z2S~v%Brq(V&R-ZzIHd<*&50_wh=D$+#=Z8jfp=ZSKObpR6 zF(*dleZxmwyHV0W{NE6TA?Y4hSb|t7bMwmI1B6KxzR;k_rI5CJ1wRuV?`B&;E}WoM zXDX;Hm-jRTc!{`*EMJ&H)JDJ4PI8_nUt+%|8t;ZL^LImHNg4@fN@K2f zVNw4PZub=dVnkt8?VFhx$w|6+mP*#Z@9L`H7cpv!M+Xq)oLcop7| zmV0)O3Jcn1W@qc?=T-fZazr!7EYwz#Kzqp9`h0|f{dk;(wCsG+n~Wiijkd?4GQf_z zT4^EZ5ho|TaB5Zvm5se|^l<%@d(V*`>pQz2sqY}V&RlMwsySUx`kGE(=as~iSgVze zG8BjL!Hf8hgUZS>*@6V#&x4sql2;1NlEmxxlxSah=W{>{#|DL0+cl^tiC1-5m7% z?un+vR?dA}TRMWQhENT#9ea$%6C9g{WHAKWO(OBW)pN=-kbQW}K0>*3edGG})_(+Z#92mMRwmaLkC(4g2HPCReO!B&8Ty<>AEP@2 z@$cA>_@0(L^YoQzgJRRM?0|_N^g$o;gv1D~d~xI)F5bP0mPRH}M07YEZ4=3}sQH+g1VIL!MP)0x8Abz5^s zNAv<^>DZWAAI&M6ChOECq73kr zLz96Q-tNn%WFL7~cgS4xSmq7-pb8HRwKc81;0fOhQ+UW}CJ3p#%6wX0EX^r(eG)#i zeba`~?Q4Em3bD?Yrhc^3*dNJ)Kfa^n7|Va?!lO^b5?8xFg3PR3&D`zIS9+vq$Cg#S zrmDDY)VB#hLbtUEv!cXLKIwjyOQl^mW7^d@jt!;F{kDpeBbVcA0;mwwJKtmxNW?X# zyD|0?f4Mz&*v7YnZDT4D2?>eZe@a^GET5O5iekr)_Z&i*4BiXXFD4%~Nmd$xB%_G+ z3&LpFk#oSqncN+>pj zpcSyY3t}%~A2io#ZO~3kFnr$CW!bDV*2Jr=F@D#^C{61p(EDEHnP`33?NQf6=t;5b zf^{cbW6Dj}uaYKwb6%az++i`I{>KQx&WUVg-|M16c^W<0rn6WsleC04HK2u6?ztP* z^Vx}+KN#p-dsMlrd2$fh({jBa?-T7>jm@Nq%FHY$a)P53sY5-q)t|c^^qmIQRm4%5 ze|On~(;U190m5OA>*L?CI+IXP@RSJbhY1M%NZ8(K;|EGnwz9VeRp@S=-!QI!^|nDNV-w)L(|jM74+_VS_3i7Ox9j? ztNI^dxGCxbRq3lA>!SfVT$i?0Y6D_Ud3@#=zard|QvwAWZKcGG&OMI9FE@AK)>?~N zb$Kh!ggR7G%VsWq#iq|R!C;`fj{Gq9`pn4Xf2~`;+8GuO!+n`8%l#7h(IO2$@U72b zUAIHeY$eSktSec@!QL9&V!A{ZE$m}4GbR!d(Vsl}I2-SL)fg~(fGezeSy+JVdq=Dt z*bc9-*mn9N6_HBg<;|S=4XiZ6W~%zb#Vg&<>(De^Z3R zg^e$JsPVUaNf6Y@c8UcUNjCO^c%8@m`CdE3iyp_iUICjoFEw!U_HqI9f|dX$Hc);| zP0b7>#qBz~jMezSP4_L#{d*dfTr`gPiLBhXAjOcvpcZak^7=#&7+HwVk1wF8nWI3J zd5TNb|Enb&ArXhtE zEEOCwOA68^1+HH_!`)O2{G6?&4Yvf3nJzcO#~sAiZdBVxzM{hUXp0cV=?nSg0@Q4S+8zhyq$%mA4#YGrp^v|@2f{<<7+8G z)V|Xo<84xzCMFBxuBXsV>Y@avo>r@8R4`RE=WNGM+%R5rz9-pd*fNbP;i)p|_Cy({ znpnX+ZvptMFNw?7MPOtPzf_JOn@%$#$1Q8*PmNr(5w25qgm5w6hxt1imG?TNw$zRX zP1`-$46)<`WBbS0($aES(^>|I34;?a0XU}m3-i2vqZ;M*U=kiRQ7pV2aL%Rb$LH~O zSUbPU>Zy!|>YZYJw(Mof)MMx59auV8kr&R#ni^Ugn)gC|&RFF{`%E-BWs5D%LL!r5 zX!Ys~hLod7adJ|MBR?H16~2F2a+&m*tVq0Ulq4sD$l$t&ls$7p22a1o$!`rnd3kx! zoC_yE)yu)Ev1?PHG1R+b&cl#N7wN1~cdR7}W_e{K&$IsNJ=zrFYvJ`E-8K-*E0PaH zr0T7z*ReKtI%VpZ_OcB8dlva>68R;Ezd9Z@`U2&;3@3|cJx6CHFeZNGhmEamj8?Vw z5<9Y-gSnOT8hbihF$rj!1+r*WEm{n<~X&L2QKd!Ya&89v4|GxfsJiAtcxXGrI&;h}29H=aCg^ zQu`}ncVY56ifnkkfLhIP0;WM}xCkVrHA1k9yYrjJWHLzrk5D6Joul;1Z8*9ajXzM5xM!w2rsh%7zqh3POF2d$>nAFYg<-UwqfL=W7eV-E|T!Yeo%3f zAQphD&{m4JvXpct&bI9dRd7}1YO$EL|M;=7eW&?NCnhkt@w?I`R2qUA*drk!M4oQ0 z*sp(0q2No*miQ_K-L;!&P5bm@P^%2hi!(KT=_`l3X=#80h*%IwO_E{2I1MHUjRHGqaX3JUpz| zOf+K4F?h$(M;VB3lJw#0AFZfAU71cZ#$OjNr;_$IaeZv1SEM(rQUQjgx3q{!^xERr zz*vJd8otqn2{h2MyB)j%Ib4DBACZ)+)-7W49QFq$f;G!wQ#89PnjFX`D~DCBY;s2Z zzfMYM4&r8tQO&LVz(Tln7#$6AKSSg%(E++ZW1H9WjcjQRFg)wYEvv1r7Va6E6m^k3 z*ax~U=w1re0zgqsCZkH+b(~C;>hLpq$PxZPgyX<+{{isqkpK>E!5pX%%)swZDK97j z^6TeXLsoJZO$()=K-+Nr5NtK-crzh}^kWNjJJe=~8bXAAIX@r%xv0^?ZBJVjGScIZ zl{AjYou`Z~17xzFdZU1;MBFH0t9^+t?rA_OUS|>%>OFa9Q&3h4(L2@><3)v#0TRWu z*MLe#%a%XmJ-r$Eu_2?VN-Sv}S)%Z?XgUNTbDO*E>$h}WGL}BC<;2N+b>3gDT`KJl z7@@ttGwjVWs%u9|ic^%JtHaYnV?!cBM1}r8+>HtNrkWbu?%9z_MEk?@${u1xf~cVh zDs^L$9Sz-y`BFirMOS4!)i%)ZuCBRVIw2Wdr*og|tAloA78z02S9E?MjE#OFbX1)I zG{frXX7qj-EU>qEN{u43>a!(IC7Mv(3>VJH*|`mrIL4YyFQp<`)Q}(*`tLtJmx-i9 zMUA4&zkWxQAOp4CiO!=~%($3@PsC!ktRyhjXnV;ONuq}QsX}FT1gAhbb+&0=k2{|l zI;g>XBoW!=I*PIX2)gQ0mcU=t_0`pAAcnPK{R^Y*nU_wzCVW8_{JD=<@b}xIEEcV# z78!sXPX*dG_z}&Kt6Dm0h*k;^0dt*@uS{1lFH2s6(2dqjS(h|qu&Tic?U{&j&63AW zW5-0%s5Uu1t znzTLmia7tmKST=A&OJ#=mSrFaExfwAx+_jzk}q0ZNr$<%U9d+?bd?p8h8xh)j%8jY3Oxd`WIXJE7o|gZ7(dq%S-#iD)WrM*>mbV zOp$LkZUuXt(F}jpq>-=G<5$&#L3q`Kf_tE_D4dA#4pe*-?;Ee+pQ~je!mnrk!V{v3 zGSF}wdb zi4*eFj_^ko#N(UE7yRE-wIXDCgDGMq?5j8Quaqz}!;X%p*|EM(a@XU%{RuD`aZ#|K zLCDJbev;KL%Y-E>w7ITG;3WOwWv2(v99M|iq>4si2hSuupVwGZoj>NyA{8EQn$hOV zOL{99d7Mt#ytch<^aJ^`A~7D-Y#Z>Z10 z`AVa{D9w#I+MXnzDE_k-9?P0Pb4x9D50I}Z<;$X>geBx4#JP@4=kIffynO}-`|pog zJ&ADC`6t>SV&Z7fL;AYI`e?89!TgQt43lP^Q1NFKR8&W&MVLYIGf_N20E1U-!$=^| z6G8{UF>Rtt8!-yIC0x7WKWpn3H!MdKgSkjkQdT?1Hbn?uQ5>VdyqR`mws5hKd_guu z*xQ5HoOsZZxNnuIxH5|Byp4Y6o(_hNfXI9GKA%&G>ugPAH@}gC-|(x`tK`$O;)+f{ z_C76d;T1R$`b}Bn)a>X>>ru|645v{&0*WUE3#Lf@(u``m$3D^oUUZ7GZm1J9PY)2IlwdMnxV)l5iT=@r z_+&B>%a2xD<3J$>AB5XcQ#uwQHJBF&q##`co5rb~k^*EYX>vPEpHuVXxxKMpNsI8s z?tSP~3v#EAjo)Mbj*A_TYMB#JlB@1`E&sTqHqkPxuW#O~AbbVT)Dd)=3pn@fgHg4< zd1kgR+^dCI^}w8bv_OTz)wsFj;L+)os|O>K?7#YP-P27T-Urha9;@c#tQIMG$~Hug zyTb_TK*=LGQ3;jg{L@)XIt!%h<#Qb4W2r?ghb|e4RNsA4!FTb>tic6DNxyz$9BG|D z@r5=xQ1Em~tE-N4y@dA-;Zjufb!Eo{^rFdtk009G+DzrEy>YA$8HM=_QX3(y@5DR}`LpImfUuQH)t<6~|O8?>}iaR(=PMULlU^veSV)Nocmjtj0 ziy>vxAu*?hQPaB%P#d=`5S(Cj1G`ruzV=EO@V$ANvM`5j#YDfsUT4-eJOxIg-$0R1 zb7~`}r#l(it!{2+GqA9r3h4jo{Pt@nVZHkq4H+5v=S8bm{$THlaHy&9@jzWBt%0#KGYz zL^$x}?xDpsjzy~T?OUKBNm%q+viJ5I*(3|%=L)8NR+|pE5(Q_CQ zZs|FeL`eZ4lLHKv`e*d3XvB{80Y?$ zznp{NR>1Y8cs}Y@MGmWqLC9lan*5&t{r#i(hpEdLyp-qo{Jw*2Exa;Ed|$_Fl9wo(&J7u5?F6~9;h4

ziFyFw@?#b76Js%3ZAjp|d|L}`5?z3((uDL*)jqmtvU$VN%OAl&{tUAM zYEfi2ue%$gSTKj($$5!f(E|r2!PV_;wic-_%-sDZ1OwS@>Q>FlHij!utt1$kJ=P6* zY}iHCN2=4~u2VZD?}z(}-!;2{W^##<{R(VbSAgi5P`^Tlh5OuYVbn{W_=NhtP*tx$yCGH3t?Dxe{z_ZF1>}hXzeMp0z^0&$oXZ3dsKco z^^~JQM(%?+whWxK=@B$qTRPDouBbXv^R32@&l5DS!zqM{UwCY^9g*;_>mIhU54?6V%chR%<|xExtzgw zGlN$pF0GC(i_h3uk6n++QBSOafOaC*0o(j%Y5CGxBA^m0aIyS7?2&l% zcxd|d;J{p3O*w-ycam9tF`0j%qBjshs2F6GV4b^NemIvm~ zB-K*4;j_b_!rD5@IqUS6^nmAIL5%n{FQNl`&3%pyx0xIQI8-j^*r z0Rp(!jRK251+)m_gry_TLPlm<+TYF0tz?H=a7Nl<+*5y^{Jj4;7>1T8_BU$f`Z2?o z-pZu63S2Y@_g6j%V}BWW!e5MR?vM{K{G1VY|v0p>X26eEqaY_ zoCRZolY(-wMx=E@s-h7S0+r90@H)6c)PApkd>U16H7?q#Y$RJ zSE@%YIA%1a_ZW<>5b1yz6cl8c+bsh4a6&-Jf_B%DM)}M~zqR5}Ol>Al`P@5too38= z;Ta!4D(pumXQAJ6xYkxU?U9*58^LU+VRm(&$|4)7mdt8E2O>fGt`d1_kzxsu*q)BA z2aIJgpXn{|WUPWj=(FZ+x5yC$^kC;!(^Wxt13{r`cXE>f{;Hd2jDC#SD|JbA+1wU{ z8I4UH5W4(YxY;byjBZruL*V4deE$U(k`MkdsSM{HbdzCXg`p@_Sud}Zv-(O>Q^ES- zlngD}IW#KUkru=zFuN`8n4CuyGgD$CyGM}`K`SU2^skv1+}MdLq)*K=+v1MNMamr} zToBYl;cI5DP=)wk=}83L=E}bY{iPSZkyWEfi7M8LZc1Ji>rO*zor90X)>hg-KOfQk zL?Y;^`>^#$)8pc4FN&gfiFoi?mNMWy(O5ciH(9?(1z*2@SWxTQilPa9w~7l{;+Q^K z0_bz|uf|6b!Kp$AN|U!@$&AnEo2TxCck8rZ%`hMei325o?(VF4uzT^;jG&f-Pfa+v zJv@GhKaPMyLuFsDrbsegmJ&- zTiq5?*xFi9jybmm>)il+p(|b;wBsJ7ABclF*yF&$fKlVlbR~|(VD}qm3gBfcep8Sx zh5vamDak1*+Vs=K)w&-iasZA-N*xb+0Ft5PbOTl|>tCvb$(U)|LAP^q*;9-94QlH} zG5*fBWm==Ehn~%PYAR9W8SLUabxmCA@H{jYz}3Xyak|=G`wiaV2A>hZmH<*E4c^4z zaWHZ<)L7=>Ut_ms)g4@+mHGkv!tPq_q#P?v>3d^o8g>wQLGPEto@GQJ1Eh)3M%X@t zH-p1de#HJYg&0iOeXx(v*HbtaUE_YSP!n38K(ts*Qe{=gNKeI|m(2!5g%Ao}(>wlY zuvu#7HVw3SrN}LCk+=xFVo^iN7Uf0HHwr)~I41^r;WXQ1+b>2{s8=DKzI=sef}>}& z|3kSz9M_MayNV({luc_zC*AmmV@+Pv&cKFv;8|dzsfS%|2=#ZdM9fYx)ieiUPv2=K zg5S4EEJw)!qksb;95#OA`2jd6^Bn!|n`SBWL-f!$BGoxPaaXN53-TAEumPfk6x3ry zebOp;2LZy%KOJ%aqzD&Cc=wucDfE<~jvfb_-z((VH&YetwRO=n5R1Q)zs5XMb5QzJo}Eke#<&8gL5{>>LwUI4VD)F7L=9# z~-edO)`VE2mGO5(MXdK44P}4!_jx&zGrQw!|GSY?kA7UQ$$P@UqnWjSYP?ksH z?P-@zhgNPIaRV~@B=42sH$QExI<~AYMGyY7u9-mloM{lgE!>2wq?*tvo_RN-Q**{y zruX$mv!3BsYtxa7b<_0hOygi`-667IlGETDs%X*q#06qSvjk}S&^pKkBBdZ#tGWFG>`|#!xI3{)nf&-P@(}iS|y8|CA6g%C1q%7jj z3NI?D6AkzR9(dPf|46^{;!rEcGyE|8OEdI$xTL1eVa9ykKnkFN(WJi}S*2_wHA5&p zXH3&!Bg=FbKy6OzDh#l=hbqz8HI;JXXqKd*ERwoBKBP(TMd~=5^4E$)b3uT1h33N# z|GrX7UpTLhm)I@>UnB7-pOHI1O?7eB$lu}G&AvvgXKBZI0wD`onlmHrZcjSdHxYn} zQwrjd2YUaMqoT7W#PSMCA>ucZSo6g`q{2z@|FYM>jv6K56 zV#!k|8;|32ddFnTnj1^Ui#^g)JPS6YM$%h2bmO_$OMaxw<_#)|xv#bfstWArS$?(X z;DFp9Bc|+eVbnrXxazsOm|*g+ zWlhnd(f5%#;OX1j+Yk}Zy_#eU*+FZz#zLUZaiU&i78Wi}_jin@x3``mTBAUK?RyYc zG20}H?D{v2i92l4xqNdDh)HV!0|@dc zKYkk$yw}56Tff9ir=g;QO4+4wdW_V0en=SK3UKkMh(Hb#zZB3X$RRe;Dy9H%S7svuwmbZhpi(v?&n1hPy$fHUGHn;Ee>ydh8{p5MzL@0m# zD40MT@(lC2PC<&u=k-j3XAI+B;-{&otv~w=HDQi^h(F>BvDK>zIM60Lb7PV-Tp3Uf z2q#J%@NCPq)PoBum&-oxyM*UPl2)mcAAkRrOlibA{a`pYh59E``Wj|EG!@x14SmzU_9g|zmScI`+!k(l5BET*E_dbGlv%NOcIHG8A{aX)N+96^pkW!fqc zc+e<3AqaPc8~2#7x+Hsp#S;_OJW|5OeKUeOE_~!G47OY;)kg6pO-3^#VX$Vx(w+Tc60MpQ?%Yopm?X1VL2@gZj~PX`i~b13mSL&V~8SY zF<~`N=uZ=Ud) z=CU?HXKp-$a(RerdcS~7dw}S+J!WvopoVsxQyGwD@$q|SvQCDb+m!3n&A9tS$Nh8X z;bBB|m-j23t9qlbh!=THe8&ElvhrDqg2ssPR$7&76S2sjQSf5rs_}o(?OCNc$id#? zY6yZj{^~8oj@dtau#KrOEr4&}7E<<}hi>$jZ}GDg!^9G8;GgV{Ty7ZIvPlx$`Ei&^ z6~ikX%fWl)N!Inajp?DYg{Vhg##ZpOPlDg=TAF?t=nUuaJQ#{bln=8gQPKsq$!$fH zvWK!1T#yR42#e+mpdD$*R7yT@gj(53QCG^>wh$4^Dxft%h>0Y$@&gf}OvwciZ&W1x zg2$V+#4LrS6=6Ld!U^nw95FW}y|7ou+|lcca+wAgT(6^2c4P9V*`YF7F>|nYxiO;p zb17XrJ-6_~_cW-B;L4d-inn^hK^!3#3IBzzLe3Pel%@mEn}|DT_IBtr zuL~HtzhsCBVv3MVIdAyJCqCNk*s>(=JS+2iA@(m(F}U$%4U0d_eh}#<&a`k1S^b8N ztM8E%$^Q#wBVE0j-_cLuM*1+QaO4NOTZRZb1hC$EstUnvPN6aH8f1y`h|5nA8{vMO zrwP#pc%zE`GbEqvm$n&0abY4^JP`Ev3WaZ+1~pxUa}#87YQJ$y~0VJ;MAfnV@AM_1>bW4@K<#pe~R zj{OyX6PSFhMwg6??aS>Fi2N1Im{3UDuB5Q|tLIIV+8UBMS3tYi(d&QQh*OYW5Yf>0 z3V-NfDILAt>}_5X3Gj_Nkc}mb{xh=6Q)uX^y)PKL7|p;o*LEStN3L`w;%D-o_KcL? zUbo5r4#864@|aO9o{(iQ&%#(l|4|$9M|TO6o@}MiQ-9%HxnRAbvt>Wvk_lH`S3r#u zaIDU-lO@YhQaqv;B64adpsu!*V4HocV#xLV=#G;}&v0p_d5_cEZ`t>24!1tx5CQ9r zE(?e)NjL{N@kX(LvaP&|&ac`@n3UOiHfOZYXwz<;)pOYcL1!Z(cN$Uga|yMFV5FPA zrl_>coJ{On&GBoo@VrU7T5DG$T6KhPNul?4(l_c_Us(K4$MQNL2=q~(SlbgZ>g=VF z{=B@n7Py$8@$&OKqtQkMlEBD2rAKnHyo3iroC}t63Sdap{B7 z!(l3wI3wB|)P!LK)%#ball$NzN~Y*4K6IbkGOp5l#V}KIcc6CpGA(CU(YB*&an;a~ zy%#GsptSX$5?u(%3FD}Yf8*ZPo2D~-Y(}>`zy@~WJwcetTV13+2+F(RzCo4i9FRuy zB>-4aei>i2{5SO*Y;=sfx0cl6XKVs}rrX1%-PL)hmjtv&6vlSW2sR-U1!3cEt@l0y@*_YfNL^&O^%7eU|<+{PFmi+6= zQI^`D9-J}GBUI@R=434P3Pl*zU6A{8Li_k9koJe9>T5b3N4s&O#f!-Oa1w>OChw4B z_47ioORF9A-gz2o^6a==PAl>zh)V7ta}s41vG?2yO)}S4t*_cY9-o z9V74|+nX6`_aj52MmFcZ#b@*j5UW2djpsb#;km+*7*~{Ka1k^x5!QFBBDV+*RG(`b zEq8H~e00k6iD)`)5`m1=+&Zh#vp?)Jm>P>7S%8{u!-4k<%N67ioMKtNQX7&HX^`Y=hk|Q8qsPb!l8|_Ui~}@00#$ za0(=>(JeYT+yO!uUBr;LgU1E~1k90g%5lg_`k}#gIx8Au3p64uRcc8u%P~6*@GHXA z^flx%9N)A>`dnT?9@MbG96yco#5opnzbxBE7#0mq zBe_{Idz|C&o8s!W!&k@+e#dJ@kIej*bLr)T*Y5Z7R3$z*URDZ-EIlH?G)Q5>tp~`) zx!)$RT0exv2k$}Z!qYwdevIEfltwN ztDs@SI;?+$(Dk&^E^B;37V?+v0sw(L&D&;ZwkEi5PTH29V`}aUL3@jGra;RCZz*XfIdts}H;=7?4Dd zjW=IzDNP`Ke&k-^==F&aLc?;BtGnoHQG}Y* zJbc zbU~V7BX!l!DJ+;V@wguwRlp?fqOitYajmg#Atwo(|V5e1lv-GT6RW|e;9gij4-moFHefZ;7HPwsiq>}Qi-uhL>ku@mOd zoKFV2f@I18r2>FRR(U4tfq4%W2l}sAe>H0%*mPr3gt9hF`tK@pM(9&L{19NeA+G7J zkaE9nJ*{{fA$=%ME#TU&fDO7jf0RdzQw~ovXx;*zO$$8ZMie#66O*;8Aqr=mYH}P| zjH_FsS*@g+CQH`jwDy3phwyEdGOkx9@rn3ps}?&+td52P=5mg<9O=QUiXsAs+bSn8 zwZ0yvAAb;0eU!Z`%Jln|f5^364X4Vy6Ci!;g^FSGSESHt?;tOKgIbXSB*h%PsIM*U z(TMKqAzu7b*yBWhYOgmQ^-q(z4U6t@nU({Jp0F#*Z?}Sr)4+5J(|Y1N0)6>wdO`t1 z;o9ZdiBGrwCJ$^{(P|Sen)#owHYHh*RN>$*vY?;a$P=~ z1<~!o;ZH{y=az}{(;w#R9Y4#Wze>~aRtW;ktWT^8Wb&Wr0$(A!3jSc*t$u5+Wc(P< z=(0rD`W}zvnN>}V&GCegI4AdU6_FE)51@9qZk+b=TWAQ2p2*W@OfDRUTe^^nUXlHb z`@ZW-4D)ZtoA8p4t-p0^de{sI`zf`zlt`Xz()N(66IzHM!bjZjP1X+8Qd?CRFthE( z>)AGp$EtwtgsXehkWrW**aR;3oDB_ZP4%W>zIDc$7OPO9@ra!;%7_pXR8mM@11d^n zhy(4keEcJ`{bMT;Lt<3H3QT0C0iqhS#Cdt}0 zCj|h?Iw2p_ZI`917=iSacwRXwLBw7k#ZbS9I-!euYt5fUQeC%{DEY?IV}^bO0KLLq zvhMy$w(zb27u>^2h0Nzf#m|icMplOBp~gel>V}RfrlGw=yMKFJ@6YW@a;YrN{0Mc+#wUFWbbZbP^4)Kq@Zki zM`dIJo-X`IDRZsTg00Df>)3{h)wNwoKxvO)C5RU>ufe(66~r}yqqtw&)( zvrLJ2JORnts>O00mVNo>0Xe^aEuD5kP65ba6M!dyv@XP?)*UT=ZFJIfnuZiP0cVL6 zE?i{OFIymq6E)qqQviwsf`?9_IYL5(0OCMKqM3%~-3#7)N>HpSP}fYWGt1 z(~dXXwY|5`$0#d+vXB=h-0LcC$&IF81ccOC4JZp?@2-fDWwrY-K`XJ|+i#dsq^1xr zoY+Kku~;74L+VImecXDC&p(;6bA+L=+0{6*(GLCGy=eS)dR)8#lNNm#JWZ1d7lmX* zUSN7!kvJckaXB!e7iR^A4{NqDq@(R&hpF{u)oP*xs@m2Jm@Il8_;D25%{26ioLi@_ zgexUP)1q0v`j7ZaMAA(SdzF-~E2|bKHgA<~D(6W{ zYQPgkp2>_wv6P!pVYl6t{(7HU#|{z2$a&Kx@DzHKMA)`>xk>9+exVPi(*=RJ(@$*FlBy%-)BYHZgOtbWESE{FQN(b}nNBZ)!Sfw+bwNe6WEt{`RX(x95%JapUR3v1)0T8?Iqn zKugkBOORmCSy9#@9>)YS$T|HE(Ki>&=i(%K%*)jaBhLpiL?4aos(FbXL`O#Rr3bN}5$B1@%$WD*!EQ8Ev>c3JoMPh@^Me}d*4DVzv>i|3f zqQ}-NR2{C>bu+)TAPgJdcZ!NV|M{tX4i{HrhGX!e>C7WH==fa753?Y_J;@JYTuK!~ z7K%Vixv5*>5I#xfWy)yX$_r)W3*#tEU!kZ1!8nrzf#`Hs3&VD!qa_4VRQg|19F;z& zi>qP4smXY2k^C{*jB%~)sQEkcsj($K?1$esB*wb7&t!nHPpMY(hqJueFN3lKa8F+U zI&}%G#1$Oi)R5I_&(HOtRh$luTVV>)3yezzzI%CRTR2CyFd>$Hc^YLqJPSt6`j|2% z(xOT$Y&QCHf)!eZg|gcOoddPg$Ch0E-On~k*RVY_KK<|9q`4Z#R__5nV;ZRG|0Z%B z&LV+0w+vU##`QqLP?5%jaW3qtg}+K5J0O|+DDbF#cgDmh`GX1vk?1u8kP)q?jV>4! z({^HsiYzLYI*Bj+DghLRnVT9LswWzmnC3roB!~9uh`x59+lbuv{d^HC>#|OdcBKjC zkVH*^#nyL7Y@B-%xOV*YMdsp1RS{;Z2U%W1nrCj04Q{>_Troiu)lN&Zvl4>%5t1pT zaKEt9wa;k2k_w=MpD+<&W)4;zp218v1}$t(Q&hWq8%7Sq5Sh^Zw~d|R#T^c!{2@rd za^xdMW<)#ghAthd)j{|)POVe+3+y9xPGKAUBHa*0hlo^R+CJLga#Mx@lMwKpZSgOO zA;=60LvKUxmLPuF`~zJf7tDx%x>=LWpA22byHSAX`SDWI-ymA1K6h}4`+<7r*q47_ zVQ7I2i_JCa1Q049Lq3~OkCv$BA31PHqgHIP@CaQOqBUD+vKt#igEdR)Z-$RUlw!^w zJb^sj4r#it!A-}oHe~xxPpKZ;(2*cjXnSk!{(?gi?`|BcTQzE&ezM$Hu=5#KH-t+7 znL(E&Z02<#^NBjoW~NPFH=H)C8X2RbfbuF0mr0yw34(4|LVw_OWv^Pt8P|3ZWzn`i zWjmdr+pXExGy!V;L-K!Rq349BhQ3!aru{D8z&qz#%s(=lt`9A5uOAL&HC28_1s4r_ zW04lco#URNW?C=--1v9VfEw&k0s`d?HWZ~NubkQH5*NPC$ z+VC_@mQFe zYr1{@lK;xs^tXL+3QH@bU)7iRHB#pf{S=`Cs zAsSxY&l&6ZwPX^VbWs`b84J_F@&^9stdWxtEcXWc9|`p5O8Z;7A0@qYG_SZ6w_f779)6e$FAFPmYu!Ec%ZWZ5V`l9Fbcc*( zDw&eMsYox8H>_&Dl{w7(nko4+X5xqW4+w1o*9i%_I`u2nfpli5ddYILYe-}KO=iRO zYr6%59=9i}cC2c+T@mbhz9EV#P`HICF#jHzAN3GfvaU|9zw%8NyI@TgVI()U$wi0`}N z#9z$Ipq?3!=V>lewfe?1{c@3eqht@u%xxom*xU}Clmd7VDKFIxg-N^Jx|I$4eNnQf zPg3J|E@Q5#rOKxTZ}wN(>c!t0UuaN+)aatk)mleB4E)Ox=KNGQ(X*W|Vhva<-%qNe zlZK!J+7e|p*dr)i%xS@Acu0?|fR4t^1hjx_Ra`?rBu<9ps^-SOQ)*=-9CJ9-3|~Tr zS@sfFP-yez715ft0i{<&HRsHbYU*S3$+D51ywd9*hdykkHpD2wSc5qWKOgPs&IhiM-;* zW>MRsnWYKqBRXIu!~x(jaM6fvBYEL9(=D$21eP)sP^*Q-XMC5hETgnxQ}V$JcUB+` z;xnAn7yreEj2o=k*jx=&+#TE{$29@Z|j=ro=;9AHQQjBg( z83C`cjHn@06j^9cS|;;OL4zD*L-Gy0i>|KPr^#Upt| zHicD85gaU=)HI`L&2${V<2fGaqLog#G3H^|t2Z~DZhvJc3ipr7&Z~jvsy?BN(&Z=i zQ`h2*($@$Iwxw)K>gny=5w6P)Z>=nCdOKp_ov}*~y7h5N19r?b9aT});{qQ(St5k% zk;~tI|0MSnZWNTyNa|>yqwQxZtN|jw+7{UpHx#`w{>LJQ+2d(*4Qm&#mpN!tm4MJV zBIYBeR)opQ676a?ab@4pFqVtVrj=y}ax)CK8R$ z13@qRq<-eQ-QmY=kTtC74W`rPR(O1saxBO$)w>llvbJN477SBdc{Ui*TyZYGz$1>F z*BkKwM54IHt4iLY4My(_q6q3dKqJG07?|i|RQ-qYc92~kcM9;tt6of~1@ByOvlXmvmNpP4zd4@ostNO zNrY=+%T+foxiD$JD60`7TbJ_iiVYd`N?L>371<`y94Lhk8 zZY4fN3n)KHKg|4+NN@h9VrqdL9cuaA+WF>&B^qxZv#r;QIxt_gC&Q!n*vJUBQFqL;Jlo0`#x;z)B(B zTG4)4Y-9vz1Ne7G6nQtzkVJzNHOLSg<^AB0MSAmSa00+UCORFEZP?y%9%Le=iwhnM zZc<&ZzPP-YA`ty9UEhEh#rH>NM!ZM^k7}`mAS_g%NJl5{3s;WEi}S7csvs>suJ~6i z21k@zUr;B6zSr4bqt?0x_wXvS|5;~*uFnGvFJpTL+SyEww!C)1%P8SBBZK3(+hE)` zVy(~^&BQmbtxBAnDd;hppq?ec-3g23NQM2r5*F76(Xw^bb?QjSw!KUdSJP3ta~M&s zy8FdyBI7JFN`12v%N3`sl%HV|6Q6ZfVf8$0^C>8upm7T|@w+3%i#(XxS1g_kOT5Qc=3myd zrzcQO(F>Tif-zKH_TWJB8Cl=!XOS8*e5u6ct!}6}i>A zYZDHymLV1&s2kpH^=6F3_6Q~9>!T-S2}cyOC^hR7HMLNmWnG;=qXMbVaFd@VT$CCX zC{j5pY?FOV+FUGzpfn(2o=Jxj6+sD@P+nyn7PV? z_5};5T+G^!`)6j@q|vkp759y_$Wc6g74t0brmwYLsAZ|pXIS+cHuGA(q<56rkjnnO z-*iPiV!J2$8P#&toP?OCC_eN)r}D&R>g5nX>vET=X@-`fA1ILF5hZ=uo_`SCl&Mz$ zhSJ%pZcv%p&zIsMz>HbvCe8I?>n8eiD2@$+z6q9Ob@R2K5I^=eZ5O{JY9-Id`67Ud zNu5R#Tphj|Y)P>ofN5#sOiBTR>=5;j+63`|M{p?OYnA@mHAk`xNu>9VIfIC@uu$3Z z-xXqfH`75!@vboQdD;vIQyjPK@S@A_`O!s9?Rv72PzFv8f&cWvFb@2Y<~yeBfbF%% ziJ@!)a-m0Je%1wHMdIxTt({pKH?vQ>#{p49!;7L!ZR`SUe^it&%yZOfmiO}y;?K#S zN@1geOT&JDzlj>eozz}E>Rtj-*;M!kQ^T)AVbW2;gw}P1cLtKuo97ts6czPut<7P& zo#YljP_uS$E9xAusFB+c#a=tjGtT%i<5JzEcwS?CQ;12grT;r>+CtM=3E@zVqgE`R zelJaOlV2_BRKzV{LcyMrG-E{5uxR~A--$tZUSa6AfVjoqrWM}^f_rX21^aR`hVwF= zf>2E(A}V8en*36i;q%|tZQ>q{vr;Kh6kMS3$8)*71i}fvXwEU5w^D1(d=HshGg@>+ z{~HWeE%Xn?_r~}*JdYGCug=?kTEmg2Dn%t9DW=>?R9I1Q(YsW95{vFLO zT3%vH4p9I?UzqmEH$zuDK{-os(ExzHQw7 zhE&O}@S}79$y;D^_5o)IrTNQnTX(v*Ve{qR!*Bk6^As|r<}_o^EEtZ+QJTAUt4r?1 z-*mFys|pw~2nnXnFuT;AR9OM^I7(UF>L2+L3fwmacKPS+2=LIDTMV2o)6X1D7A0Dl zx0Xmi?$m(D{W}g<<9XS?%K)lrZY1+X(%XeXpY)a8eLSQFtaJO^!Q08DnF0Bs(R}0k zn%!fac)*x;Hoq28kgB2}Y&16>vLTabv|F0c`>2jiK>wz=LBhs>OVb(NUi*-V{(dHv zAIxpbl|!zyl}4n=|N1GZeyynI?D;u5s4&F^gR=Ir4hqCm9+a z&);O`3FyriT;)hGAN_@k7F-ZX=HeSxapU!(Wk^g@yELb>APLiaL+A~3gKlRs`dMu{ zJ{r&E395LC4`9Y$hse|478(;`%oVqja*g_K`Id|#ZIA)zp|sqUfGG80d!!L-3$;MO zUj!B}urEgAec1@20{ym0H6o9(o;@ToDo^nWN83EK|J{08FI);An*8i^_9E>`SR0k( zlxGZNG1g!KJ#Num)z*Weue(nGH3|$L0Tyb%@(&00XOlL-HH9tH3$ED3eBks!Mrk8MJUG zrmW^sI!-QM%;lqRy8m`tHLJ5@DWM`UAtJ&A2a=KfzgI$9h{FbbHE~I< zaL~d%XwU+{9xsX$s1*Ze6bAeMopBr=hSiJ@Zb;yr@MYQ;o-TEo84lq>>EN3vR$jyek43KMtdSwoL0AYig;Sj}_@gxt$nY0hSP1hE0Gd^6Ni7+X?qkUzU6j5! zWIGNw=GfInuVb7Dcf3fxfCogD^=(+z29g)<2d0bM9rP;e1ISq%RN`n4QwK!cZlDix ztpIn9X2EZW`o^f_dF#!eV(dS6MU}IX2dyk zdopDv=r@mxgbc5}R(#zc+SYH3P|~fsH^lLl2kBhWnC)Kw@UfkgJHD3s%?S+$TgSx* z%YxXxL{c5WIv3!aBqtLj*Z*fUSL&kT(<|a{iQGe?8eL+}KL!J#vxNI!9ZgN&&o^wR zu*cG)s{%_5v)&?VY*!XGFoLq#9%mWF&{8{4=bX*@D|1zqs-KmAn4>rZrz-%i!RL5u z6WR3J3M}-shFf9Lcwk+`X7 zGh~qjj_Yv|N*`elUx2`i5VDQw#IeBWw|r`&jV*}kA25QouJHSGbZAqA1q91wqQA!m z@IvR#HNN=WV{5c>g=SqN5b4^5=Kl4`{i$NpLD1)c7u+)cj>)a-%bPt;e>`}(F$`iL zDc*f%F^FoR4g_mBQz>l;0?A5^M-X%?o%#@eWDHLm_mu$zZEu`)CVajg!tRVE z$RIp@izATPn-R4z@&pK(2d>qG!WY3Hq1w*?oKDBo=h3dI; zC;kA*ajH3fbUYC)f?yRbcD%;;4a2(TWyh~gR@lUmen887jLQL>T;b+ID@jJ(bS4Pn zvU|l+j^Ba%0)1g`L|w}#PZ*gsOAXRqG< zIo$HMee>^vAuJAn_P+z{!!=6qf&^`adm)LHTtazy^9Ad zWcUk=0_{%b{%|KndbUCj`BxF$7%wZOurYeZlb(UXl-^sBqdqn-wdl+v852HIK0fU9YD&iJhxU&-G-<-9|gm>3F|PR}-{J z@z++ZcH=|ie4U)y)R047HgY;XV(8~y*?V9Z5G2ouA&(A-C@)-Au{fAn`%;D&m|Gd_J` ztLEzEpLemd?2o3q?_k?re*bJJbZ5OS!+P#AI^O0&;+j_j+ZH9k!Qn z|J=uPg?PT}OU1WBH!x;&8gyX~h}>25G{+A1NdrV4&n{loU0O1{U}MyV_NKcHzOV1a z2d4HuWk`B5?|bz1ThZ{WI|RQ9{4lVp=LOj|OGJTo!Q4VnOYp|p)Y-sGKPB)j(bj|A zPb^n&#!OxCB$_%70-{jrX9#G|3&ikMXwM%k5EeM@f1m@mCFA&ydE?Kyt843ii$_~a zJ&qtyVas4uSwV8e5Mz(Y7ISO!v&Z`@Ha(f(iNZH*^lm`+rw*>PU8Z7a(JQZM%Abd7 z{`RwkDBbKo1H0Xjd=nydIZK}FyxJaEyA=G^;hHOY?7tl(h-pxhoV60-*puZ>5V=?? z&Q-K~xKEDl8V{HhY^Jtw?OF6J*0?CLBEF&qwcZmG4T{QINs3IJJY-&zg2I&@!Ji8= zMRa+(&+c0x<;h--6FtlKxE?f<|6Bcmv3Uo!q3Z^!1QQ142RZa3(7r%Lt9|A&dxHx z9UGMKzVAA|-9_Tmg0M4x0pAM4*CqTC(U@lq zjCYZo_&=!l^im5+iZ)wGn(5{8ujiKq&pyt=?d9s#zR@dX#8(tj7jjqAZOc?3c`DeZqtpiBGIFi*a z(%@*4*dJH<1lQp^cqM7b2g&=kZ9as}WEc^uZ+T9wT+WD|(O=1P@7q7Gh;B^#hGNjC z5x5%{k>dU)d-NV5vB)v-+If!J1u-Ks*&DBpD*t4d@rQBy z@8kpYDZ)Fht!EgH!|sLKU2(c|q-WXOpc>VUyL~=v0rFUGW3f#ulndPGPuS~+kav^L|xH?MUTKZ-m(3o zI((zgM3{@*oCk;m&Gzg68(l8@7y2VJFey{nlX=B!L=@AT6sG;wH@I+<`h*4MQa5E@ z4~DCcQS;N4=uROO_s+D|YM>{rq=_wDOBj(`8v;!6Cy}|WH<>WquI6eoiuvS3^q~Ib z>=>T2*symeo<7{M=TxbMO6V8hO6xx94<<7M9u)hu= zSwCtt!PmVG0Uu6kpW~>E>BT2kr1;RxOlNbz>H&5!vi&0;rVZw~LwfkO;FLp6LNm{b z%c?th&z4@0$V~vTRdh>XGqyc-ON$TBrOmrIQx4z#wRugw0PuLZ*FP-xnVeyp@CY1p z^YYIH@gY#wD5tIkk8gf(YT^)v+aEE@!C{SYB6yWV7+Fs^eOxq&tN}Xc>-Kd3$wUId;~Ex^L!dL7&`$(*7Au#HC<=Y1cf&a5>*L@G6&b zVPHVr(=TtS1*mG)|A6xT3s)biQ??g&-5rys+Jq&Mh51qt!w~qxUKP{b8>goq*WaM; zv8BNWd!fxB^kM`rmYRopynW!_O2yWLyQjua!9YQTz0J~k@#x-DvI`C)u7b(K3vg~E z{^vklZS8y#ctfNv;P~z{(}SW>O0*A({XnNOx@qd?>o%e@;#i+}zycN1SkT54m=s}o zbo{XfYed4BhyjO?AMkNa>TRly4{hH$jhf`4!tQoY}V!=tYzr7Zn(*IXs+H3a# zBS;W!kA#T}HEIm`B71|;$dY*9j}{f5i%)Qj0tLi)XW8mC+zuT7_M+`*@%qiY`Fzj_ zng)dW{N&PNUQn@N6?A%Gtlx=>KP77Z)%~Oy@WSw5?pT#_M}W8;w&3^zP2Awi06vU| z1{J;$TU}io589nru-JLEprb?iD1Tt-^@cCMSqUYv2KV9YT`h$I|m`O4Rgl)1=<-X$B54_Z7A6KLlCA{YCwVf+;B zMxH14pe|}yMRv^cnP4FOlqjAE!@eyxNN>o>4q5~vp?9VpDv@kk0~}xuVur!El84$F zebKz=6xhD#cY=9vXSpv#&iBFeB6U;@6!Tn*+BM(C`>~cv$+AE7p(%*j^?4U(AFd59 z=$7-v=J(?~ahJIotOTJki# z{<7cKK0fp}?lhmA)&qIO4XX1FJuJVc+(^d&C=9S2VQ1CK!po-@bw`{94AxLVg!`b* zf9CgJ(vH)7IOOI7SNmM1W+Pj;cA~Bct9zhV*MlzjJa_=^d+}aVo%T7g%kado*M7x< z?^8Zf-^rZGXF`W(W8j)La_HBY-6+Ti7!n^CNd(9Je_lJl^N#iPSb*w0izcqW+Qy;7 zH-jIIwJ|KVZRttg6mT$>b(&tAnISWfEdQTq%SamqJw8FaJy}$O535F;Oc;TrurA_Z z;yx7QXVAoeJw6^grqBD=vKPl2zCXuY=pMva1WR3dY~_%Y!A4tKv}f5LU$p&@UjSg(>*=kgw;LM{kdQ2XB!iYiN@q0GMJomjw0urGbV#cCI^uO0we?o z5Xod46BMtq?$t#?D8XBM_TE*yPMvcve(&FJtxs$1b?olkB2(t7@-v}=)+mvkBnj|ZH-a=ZH3ywmeuyR6}n zln3{_>FR3R>ch`mgS8rXu77%OBqpv(T;ZsW)9rLMk8$EfpmyJliob?o-+fq9wa z0$Hco6V09!+z*|{lT60{cCmL%T>ph~Gy!DwsR(+}K1yfBdtFWnT-P>TfB#j!<`SxtJ z0kPZTvXXA)hhu_~600qlME?~E{nfK~E}fXMY|N&t{TrXBEgDh!Vt8YCF`_Y~ubBcF z>t@1>G0oxS=oXMXy%%I|UIa-K+QYNq4Iz12H}Gv7P7-?71sE*Y2j@Ra|@P&C`zgrrUh6!Z>i3)gaQaF*f z%e&N)&+$Okb=%naQZ1?TIH$HhcqT^3%PP6{&Mcn7)^C(msK13l6m_rp@rs z1qdhTKi!~FGgg{(&IVrw=Pm0hW0iH4^InNw=e+#yr4JJ4XKY=RRKb>`BPnvKaD@@8su_Z?8l4?ls`sFc*pwFXJ z!&~{8%4BYu2Wd;kKvDc@EEf;i+ZSUy3vzR!b}g#N%cu-m&FVghlfmQ}&--{JA#=lQ1oa{UeHPLd4uh0A z10Zl>7rah54|(6LfwU!~A#3wIcys#-6l1xJ4RauU)nv@S0B`PIN9jEZzK!$2AGHVQ z-DOYiZTH6=Z>YyL^!pbG55!r%JO9$bS=eUpD+BNSb|0D1e?qI&GWDl=#A zI+T!wXzQAy>WhHXxdTxB&4c7AUEt-|2zWWB1^Cv_gtQeC@t)Kp+Q1pTQ1a%Z4Q$7C zxD3`wUonB(d@;H?WUiYA`A2s^%8cHSgl%PQozK!;d?Oapmq)(M*|X*}ynS0s_toZ? zEm3At>#NI-gU8N=xfgy=o4@k3$?u|js|>aGQ+c2Q#u3ql#K77!Ksrt7Uf244YJ$sJ z_x1$QQX2UCS9jtMX6;$)Pnz7B8T)c#M^=F;Gy5>(UyN#w;LL*5C1V&IBC>(G`!+!8 z%w8&OY)i;LvK4Z6uY?yP5#&)#!MAJ-tId?@-Qfl1Gho}6LfYcdSf?e|OIFT2+`ig8a3-h)#Cz(A7NWAoMA5xf$4+M_ z#_og>SzS=l>@qe0wkokf3fEz~6O_m8zDfQ5e`Rc$dn#qlKxFJP&9LT>zHBtAzrB#X zc`i$sZ^d|4mv3&M>O+Z1Up}7g7*WCL{gCn1YzUnChV5G^soAfWqqHu9H@7acT@0Q( z#OgA4?;2$%?_R@qjDu8Ev-yX&pdCF087s%5lhX-Orgev$J!>I-shTVVVCs^wB+2hG zx6Z!~Q2MXnc%=5&4S(cMkJILdO*YUBp1!BDwzN~;ucbY!Z3fVmI>T(;2s2vprHpP^ zI&OEubV^^P4ZCB`fHN}w#kXd1Uh=fAP{ty=Ob|D00UF{ z;1)V4s}bxaY`3UPLG(e$K-C#Iy-$^Scnj^@Z1g;*!E3Bvg5`qfnP#B#lYe{{KbPFS z0a-g&vE;qMi9r(~cuf;C{m_fGqYozo&d^PGcEP(o6UMebs$VipdcsRRg18BEz22uhg|6KOy z)`!Ns(pmam*4;S&8>KJ z2(T7r7Un3h#tZw$>A} z`DnLgCV{-fCTX1RWpP)4kjCsXrqeo))0}!K;!*v)BAENh+x(oT`Ny_HJ_g{vbu)}Z zl7>ttC*|d+X6!wt%<7Bb!y=TDuIypn9T@>P)-Qr%Q-{O0u3x~4j&)(~fG#k3P)``t zybkni_$jolR~6bfsRwQA*MN4PS7mD3peD3wPy^c3tpaWFxl8?OFsx&9nA*8H%x_m0 zR(GlgyGL||3-co3&Zt)G*d(JwlcSTmV7PK>Xd)os+`qBp-A15R=npyHY(Svb0#3+x zZ(jux%L)sBX9ENFWgEQXxbK%X?`zQqw;QI=tfSd$k2!#mWtYc^71xn1Bp%Jig(r}nNb|`dj z(-i7f{{$M=s*F?x8q}<;sFuFIhw39WbfsB!U1>TU+otyG;Jey3`5cD#=?aU-42A>! znnC=K2=;DsF|=!wrso^@3xk^F0GEW$b$bSpnzN zcjpt;t6mA(pkz-SIskS|91L+&`a)jRPAIx^7VYIyNSfXgN|T-hDWF-J53lpujyq`kn16xoe&#@ zpe!>LeCW#iFd&cHvkgY~?FP;3eP$R~-vY@^ZU%F?8mhcqM}Q+x)V*BYZ8&L~Tmb5P zt8Hq}X@lPX3A95%S4^J(kB{wTFZor%Ii8@TWoWzz;_VW@|DTD69-a!11WfOmuxK&? z+~z;4R=X{4qwL7&d#@zg9xA`gYE)h>?fmrZt5;3l1@im~JTdJA42VFe}XzyHOW1u*G2jq z%hkg=RG-Mz4{ZxW`s9&Po1}b~wk_bvomtMf!1IN?P>!N;g71C|PbS(C@9!HM0n z25zp}-qic-W2jx_Pt3?_$n2`jd;&rNQuR-A{{ejR#}A;&pFV(wC{b-{doY^&zrpRT$qGsYM+a*{%unYfu$>*ZBxK*ZL4z)vFGTs(*xl{yS9q-=)5`FBERt1_GgPM%;+_qT*c$1s0RcSu+0k1jmrm(ujPvV^k z-1R-?Wyx-X9aeMSmNoo1Hi zxqhe`p&dP5zQsZ;~dq$9Qc!WN(?zD>npa=Ehl&hiR!Zdop;mIzy`}SsN%QZwW8Q zwSx5JVX=kH zfmyfu$Hr;mu_Az*Rxh)pkF=4}NM-psmx_vt{+s5dy-a3%ngXN%Cj6E#f-q5nq%*t{ zXdeR%F`oL_yyRU$-g;xWE%ll7=)SUjdT``KRIl_qWy6pG<6G5%U7hQ|_sH1nT`M8? zoAn4rM+BlH&Xi5v7J%~Wf<7?@?8@z^@ zxp^UEZC?s+?p}q``-$-CLKI|fT?~QK-|)t8*@N4>*zH?06-w@04bB>l<;9wv1-~O1%wqgI7C<`NTjz3K4sPcjOCb zH-Z3ra1*k3u3~TmKspCQYO)}DzuIC#0BL)P)3H+{t=mV;qbBDQ|H+K`jw{b3H?ObWLpmK1cr7w?!SLcsH(d9FcvT6cs?pzm| zRr@2G0t)(DVT_oQ^gI+mTO#wb8(2KHg~wjVcBxsN?($JWB+JS)?yPl;H z!geeZ8C4K{2vz20NS@xE)m_$>d60i%52ViS14;OtJf#!(@i}YTLSCpQ6-m46w8N2t zD)c2vPtwG8yrq0HBUnpLG(S_ zMZ<45|5>o)#=D1QW`tE)8aC^CM?KsdIX|1 z+pnBGYY^OdX7QMIBhtT`%j!!xC{0-sc^5$zm=_M?tsb^zddDJT>k{R}&=CtNGjA|T z)jUXwY@^CaJG9$APJvAA5D45CdiT^Wyt1cU2^;HK4U10x z8LGz)?hd+v6AGR`1ZndIu>(Ro48#nQ9@=M1oi~IT#>XR8PK5MTQ&c`NeojA@R^H;F zGRSP+>)SYoospLW`i{k{5>w~)V+l-|(Sx_7NF_61=%8eylop&h2r0DphtifpQhI7H zexp5gt729&KRC%wFn91RXw_)5;yz}Svb(V{~06J9s_zrKsWTPuzh-x%&au;ug<{#Ojn0TKY67)U~;qyJR zfmhJRtw9Nz4h1Kdg8#@=^hU=cO<>BS`vYUKOeEwVo(cu0mLlk@ArQ3;a&}IKz=^d` zcySlfUdY%GiATHBN#V*ycPeHxKRG6!0IOfP`%wjN~@`hbSyTIy@2E zq;gBKT?|7Gu4WrZ{mt6Bg4Jv3qdRQ3X+JP+@d(J>xr{yCjMWoZN?+fmS2Iu%JBt(+L!i3%cRfJgwP~_ZtK+*>oZda3X!dgmY-47NInqjsWv`K!k{eDSTF&gX7u4E|#~7|^0CF;GfpOD{&T z=UQ;-glBPEq)*=qm_$K;yar1A5qpo&2$D`SW{uNz1(7h(#pa_6eKm1OMIe7%2w1L8 zs(b3ZAJt+y8hHP)?NEI45})y;z&r)jjsNHl$U@0HKcEqOHM|WzPllqi^Pwbe8I)c| zw>_R{g^S8K%_f~j*t>@iqI^&6PnQ>x9(;3g%aDB!=XkE7o zoLSOe0f!7EK*}zomx|ZtM`yv^eN*Aewh551VF z)wQAPLYq;w{&ZuU{x%S3;sq<5{iDz!54}Sk3<5@924$jqPfJq-UdpsC5IB1fx;FhB zzWA&XJls23F_@Sdy9|!68V)_%)Pp89KY@t4m7ztQ%Fw(v-s80yUYp}}aHocFcufyOl|!L!4&3`hcyTLLv{ zNuvA(BRb$@sAAsH6x-?Cqy}_tQ5#y|yPMRkgcC>Q_h|JwoLfKIOpqhXhe4xSRiH!j z`j8ZR1}El1UXCliet|!{$@zLIq#&4idsd?3b0ZWyTa9|2^?4eb_cmZA4V+~-7l7WC z7ZGE2YM$M=@y`g%n^5q)lndoK8SG`!LD)=;na{6wJ{nkzBf_1$% z(*p5wc)OX1>T>;rt`N0i7`!|=A4+emgR%rv$vA;Z6W2m2rd`?^37e+%g6;@l`$nH} z9`!M9OC+BrAQ0D18O@;xEx*x11vzST+KxgT3WJCT6}ef8FE(gi6>8G_r$mT6IIt92 zX@r%_2C|K_GED`!mIrb>hDci&qVS?T`5DGlSJ-Ju!Js%}RV0H+2WKgGj_%(W>g%2L zPR(kgt`EiT~FFnDp(Pz^s8ta zUvnEP;oiRK=7d~3l+u@UfEdk0){Z3@YaZc6fRx#N*>NL4%aVW4ya|;!ldhl3^0JFT z9oaXYruUDqaoy{}BDO+|Fj(lLc<=Ip|SL+ut9Rt!r3zWSuxkHqX4nN(&+{qyN-Ug+T?bP+I8AUI<){AQ{QVU z5Mu{4=G7WnBJ^#T&o-X-A!qdBqmwz`tXBJ@UeFBL{G%fn{+^oW!h11oMxd;F1*T49 zugA_XjpXF~T}e(>a(DpkPUJ#q$}>L6Oj|c3ePrP2q_aP2H`<3KaA!ypnA8MSkY>P$ zUJd5{RZx;22gEvuO$ z2$Kf3ff56N1%zfS(O2U;L5DgYL(gW_A?f&B7ej4{l~NCQ)C#jtr|$YI z1rCpLF2Xa4;Ch@88sGmjBeDYt#57bhdoeAe;3R< ztO8Q+46?urr~p2D=TgYtGz)_0dFJd~#`b{J3hibWUtG-+l8a$OME&Z_gjvHoaGC?0 z8yIroBapF7pZ)bT6TS{ zYFkvQnNTYr4*{?LnF4S^pO&gk?0?butvL1->e$A(HXKgS2QcYNMuGqI9<@JcB=CK_ zcwD92<^xzf^gfHh4m`DN3Fuoo7jzKUiBPLRF`OXj6A+tM`5h!5pQC`#?Ku*=0*cS0#2*^r zv|)gA%GeUPd1`mlHUZf9lMldukyeG=6N4uh-sdBL4YUN#7}(adSA1bPq%9wB_94?2 zkK|JUbf2+qk_R}i09el`l@$g4VFIAOiqj$=l`<}bl`VnZvoHGj$uBTIdGbEMY(Qz% z;pNCC{B=+Mv9HksTf+y9Xse~@?0RNIVe~vGj$MM@XJfV{QG|^XbJfP zGnv#~PRvrH=7hOIg(s&#!SP7b4(D3{J6A<}=Jhpg)3tG>e}@v0Ku`O0pL+pqAcsJe zVg1yeIAIp@LO4lbLDV{Q*y!`P7Lc=NHRSHa=SlSQC%x$$jKwdv=5m3F<%)AM7HJ%0 z|Bvy|X|`%rKeZJGKzm2x@37(5Ucl7LEC6Zs<@rj23qr*js?8uNen!N*O^wrJG zu);I5aj?r_+K_h4gkjy9@|vT=a5s~m9G(R&sw(wKk~pGceGKYX=^)(!LhAbuj)0=* zneKF!>J`et@c{Vk-YL+WZR{WTJ)=6;$MVVvGT>=7O3%LNMbM}=d7+iz*vjFYUvPYi z>ieyO{7D>ruu3}|{{hHOVHEQ~;DoJ5WCRR9zzv~?~A z_W-3Igs!T)XAgITJWx6fwv7VX!)(Ay?%(7?OKHo;@=6MA36eGK>J1<}- zPwb%1xX`E0wBtUZHA@X`>y+{?0;KtBXOa?NjjEGx3G|_#D0R6D+Z)JKz3%V zG$p=;1ua5nOej(3vUe_Gr-i;(r$y^DjEDH^+JOzB9PNt3h!t>kd!+gf5FG-V)do0C zq#`BFFx`>}2P6fcV&q&1990X?j?9(mE^ky0Lc zz67)%0sVetb9g?Y2_GRzTRu+l19^~tYG)Xg8Sa@iS^@t37XeAYbK#=ZstuNnt_df~ z^7GsLRRE#{X6;zOr^o1UQ84-d|1t#q7|)d2;oh}mW)Ct>e7!YmHq)!YW($eu=SpvJYuE*+E%F-lC+dCB=Ms@<< z>WTb|7j!t8j+D^3nZVh7VI(lzJ=(xPe(~d*H~)tK=)p(;WR1xgx`6p4+N$)E@<2c@ zV*fZ%=jb3TIW74|w(;sbpLxN+_FT_~@N~@(#YD|OKhEBI)j#G?V&k-4p%P$Gh&0(u zH-v2-91gF~s{K1D5@hStY61Q+~>&ttxfzY^zGc95I?jnpI_yl zJGr08KZ!!WFje-$Lv;H59=>VtJi&ZXW_s}P%~%GIFd(2bc=k$}ay%_G4XU~l#~vhs zUK<$ljM7(uUKafZS^C=Ps7jZ^8(Mj}at@x&84M@dd;(WTMetgqksOQ#o6?#h1@j}n zbY{6`mwbIi5&X8r`QyFXTN;0&w-@9!^=z zD? z*uPZk0+0&Qm$qKOyhJaYeT4x2 zMq}dIR?-+r7~%S6I6C5Z1S960gSGo--0I^lt$% zy&Ay1g~L$th#87GOCq~5GsrEE*)Yaoh)WtYV=bWEOm{#kj+qO2-}FaSI+Ryq0*8h} z0Rd`(X4i28>PVa;efGOnz|;Y)S!0kCUfB|>XoSG;9D~dO4D9?_+|ie-J=am)tC@aUQCqjv;SO(I%x{&SFiqE;=uO;h$^(? z`}r9<}m)V)r>OefQpLh<=|tV(HF&Fy{Cc9aIrI6-vpfD{6rmcIP+OF85r0O`BN zXGdmpdwD13!99%q5^7Maupr7Zy|PI7C-jEXooc~Jv<-d?;T>Q)PLe78TftPMiG5qbxLz$_B&zA5U7En~u1#S~k7h7^ zP+M4zl7Hr_F_1-PacCk~?HdsFy9Ll4^9G=%P@j&q#1JBgabM!JP7vL#He4RpoyT>i zOI+PP4dk3V3U6*+2H%Ex7|eggUq6*+rL(yS1(~^1c>&c zw(ETUo7~JyH)tLZQ3A_;cmyQ~TE_Zmd>ow)ye3W`3^Bc%Kvb(wAgc8zIKVn^ac~>9 zhh!^GVp?PE6wu6GJhqEt2SuG}2~d)@3SyD*BYU@GDX_ncmEV?8zkU(=O}jih*UHf3 z^J*}4a63pjL#sQsq&gFa-W@=XxmAN2<{bl?)`C+VYrruaLsaWZ5Z$8zobT5h$61Lf zx^r##Zsl|cp56;7^i!7Wra{@G+fa@Y7@mi25Z@K#Z_>PX{V0Max_1Zja${e0MSgx8 z%)h8a|2hKvH$Fvh| ztHSVRwH1IG#D_=JPJ=!BMIyh(n3A`0bXhlW&UfktG)fclrt2$F^Lo|c_P!Y|n`qQC z-fy1SPrsAwbyZl>r5T*=T!+U%biRKCTpZdS??2+_vt2)jYbcdTQ@bgj0Bs14Q1H znCSImP+HYd24SX4qVz!{X~zqeA_cs+EL~gH{|u5(El|?VlV&B9_;2dh4go*tiarOv!Ho2?%E`MxG{(P-~&WU8`ok`h^8X|d>Wh8^-4%RH6MDk`NB0JX!YnOjv-;{V4Q@_So*Hd7{-&6 z0HkqUMSDo&ezIsRB+nTD8LKBTZ%Q8D@toAQfK$gQIIX;(pc3(3u+SK>JrojQqbvXE zLM{L${T;1`Qm`Hf^+ZV}lx3!>-`BjC2>uhhc{L_(Kr@IR)rnQ;v51eE&h>2yaU(mS zL-QFNBNd7UKW0dKh^R|vWJEI5Y~QkQC=`+hiHzJhv#*f?qU*b8`vyHu@i28cS{x`u z=YrNKX<<4iW-;W(EP;ZHOV!CY+J@4rCvPsOP1}WII`cb8I_U=(o*$db9&XaH+0d0Y zoi%t`>U(@q4Pwy@M0c;xV>lY|ky4>U+CdbWgyU$}&-HGM9%*-o>)(t&?xC-L`4jRV z9=JYuF=okHi7Cm;eo8{)#YF?hK8j9PC$)8kOV>(e`718Y{T~QauJckil{|Y0udkg) z5ECF{?PPe4s^bI>W655U0>}HcgL(fsIEAxyU*{p_EX^xsAkFQ^c zMsWAwY-rQaaUf_j)kmrh!MHcJ9Xy=U8B#Zng#1G@pg4A^+L|KuOCPcDSsRq5YpdaP z%p%CzH4$E}91M4cwS?65BN)gFn`lv;?4+6;8_?dJ*)U#h+8W7o9Hu#qE3-K`(V>Q# zmN)MF4iBNnaj+YuM@^iElisV`MNXX~i8YtUY z5$96CWXHw;q!XwBc|G>0O-8N04dxy30w+{>JC46&_ibDN&qp?a+XEV6K>Inn)(KPk zV;E7B-D~nduTJWVv-b-)(XJYt#hH3#LLXS&qon~*Ryr6={d+RROvPYjs{$7ZoD~?Z zjEB1j#@!LEm~Ibg!B+zH@yu@UXhv7KAK3x!42^(0!>LS52JgXyj!=kzl6wAj-*gUP zl$43Bv%yo+N7tr!jp~iI8$+Y>10qo3s<9TiGO;gQj_k#n@bjCM`iz_?Lp1N%k5D;$!R11Q!DjqCDb(pjQ?JWXxcQ}F667LmIM4UcSflGOJb$-_FNyb6Czki13 z7noE(+}{Ml{PpgZ z#rz|&tS-ZS@Iv4#Cz^=g84p#>z)9St|#zCc^@C0s}E?dqidFtypI zmh^cTD0T=b;)_pNg$e*i;P871s=*LrZKdjp(5TY+tisb&qmgDE=ItUGz9Q^2#7eF0x^gACqd}!XY!5} zY~zT?E)Z~R1CdUBS(9fi-4l@wq~}we>%w8QClNYK2$2HqWXSWgC~`EMAfKr4^a9A* zIRTz8>O1{G6fvbH7s2{)dPNVE!NmPiJ_0U~?WYrA=%23DjpbYR70E#$x)~$vq7SD%8+RY&rlhyJD@R z=mS%-T&9-DRCq!kk#^Jz3109Fh)DI2wm1JsO+5B8n?O{%PuT+%Cx%vITmYi2q!lOP z!=0H!`2SMm?L}wm!F3(X+u-pp8x}xG;9m#}4_MyQf+AchcDFzhN5iv~rde;`bj!P;tc6bNu^9XcY`XEqs;0ywH1II~eq;XDn z`5Z1|9*vzsi)ehlHoiNbP^bS&QxJD71U$OF3;Oq1KndRk3lS*W+?j*DcBV^Zg5v>6 zBR_kapF58?aQWpF)8s?Ste*TKOZwI&kT87+duVYZyO@=i3xnH1%z##qfM8M@<&osE zL!h0QbA6h_MFcczTpvF1<&_HWbFVNrqvvZl4q8s5 zagc*U&Ki}akb|6=j9shI(d!R+M>e@)L8UtL-d5!0RrY`;5@-An>&ic>PWaeBiD=3_ z{c?ruP6&`ceH{q=2|G}rJ{_hX`OfE~gN3nCkalDz#GzwC3%wKzQHXE_!v<0*6gQn8 z(w1HBGiWbpcj7E9r$zQfCbU&9II7sok-cGk&z9=@L5rbArHZ3dQ|-%jXj&Ts{XTGF z?PwU%wVD3##BoFf$nA6b@h0q7(|YlWO~YDM9iXUeE&W^EB#C+gw8Zug_HTuK?uTl! z0fs`KK@2))S0ejB%)pjt2X&vv_lEef-BJ4LLKNCYT6v;6=lV9`amDm&1}TSjK=8}~ z@SoYwYsVTm1!6&dyym$JES4}WWR_4dnRKv35}b1}@{8$oFH1NA?$65nd0)xwDNT9K z8@I2oo`(mkXCYWM*`6Ik8^=ye&xXcnIn}8aFR9TgK05$p12LQ+Riqq(N(gR41~?M! zVR*GBpgJC_AqGwWcYv4p(veJuOq*O^m&fNXkiKs52`^expN~d-Xciwxdk}a>b)Urs z(mjpiG>#{Rb|-kj;$x0^6H!9RM+lrdtN_$%ltByz8)zO24x;f~zntX(k%fv9=thEf zvxFmm)QD9{&4i+&e?r?=Mx7|Sd4YdS)wg;A+qg3rz( zu*#Uw!K(9R!y2%UJZbC$EsN0_=TYn%fpfCsXK(>k@Yjl9wZ z5I3wNoJOyk#zoFs{Fv_W!<252xoH-wQ2FtL@&JbpA@i*kR?bm~a{sa^bgXHTd0;P^-dxCqox<)GZaK>j@1Ke7eabif-ostc>n z_;KAixW7s(`{>!lP!NoElIR@HdYbvG2Yv}n>!`I-CXq@5j%K_inO;C#0Gf700)CnpW$cT*q93A=%PyflV(P@3^745iV6HSI=R7}SRQOXDI_5RGXR@6y<>Oz6wJ zh#%X79i7J$yFluK!8(}FrI@g+yrAGC;=KmMmO_^b6%wJ+SKdw%bVV$Up4FAaU>fvQ zx!^xk_;XTn53GZi6Fb2DX?@k|0}Tva=!-a*>jdEJQ7loUb`quzLXd|msl$O@!dXd9 z4B5r#UJ5{Ra)^k5ROiOrQ4qIw2()jg{=bBgFbfbT-2uaZC3yx&JqdK0)TPy!$X>|o zIm4LQG~uFqHsN~$hXVL$T1G<~O%4l`y&fe?Wao(qWVf9NtIje-f|W!pfhNY0D>iwJalP4Y~@PtWCkf6qXS z`j>4neHxTB!z7}L$`1qH_^!?lUD%-5Xz zM>g&>nuLo(J8+Cg&nM6+A)v^4p)xcs@~{bTvilqtVx3r2p`<$LGy<&!lS(~(?aDQvVq676GI*;KVu)Gahz&g$eB%)qo)# z>%+d;Jy5DAFmM5ElO{+>{4%(S!F}w=PJBQ&s!cWCzob5&q)-UQ5#5XW+>6(9$q74& zAya&0FSc{E<8r2N1YA3?6YjrEfqUuMa6gSG2ku$)Ez;f8?6>#Rb54Ap>2GZDSp>)a zMG9NJ5*cBlXOTbwNu*IK8BtdA!a<($3$C4?_-qu$MS~*P110-*b?RWYUvZ;4vx7qR z*^f)3x^fUqj7XS1R2@&~OM!d`UI-wkKsgc3;P#?X>PHYRt$;!VQT;3= z(RgNj!I25@6u~^Pus5t2)fUF}YzhNA)Q4Uz>q5WQpTm$&4PnNB2-r5c6U43>0LkBs zRe(u5bboXr6rW$f?|8m_D)%#{e@kV9(Y8^a<3@H?fjojm{bOehgLd*zY24T6j)uf< zw!wq6EDNk`*Oh>itI(a)>_6X2&!t05zab1PKHIq1=>jH!%MvJJD!|CE7jhw8NnBY` zul$^`Z{3U+7$DQ~5zSN@c>LIItafOoUc*6><8lh~NlnI&>w!QIWMEF9w8fx(Oqe!{!U8HqrBo``6fctsR)=zo@hv= z8O@CXhCNP8Tv@H^dn2j(`8Ua%7WorKbz(3maHlZfWXD>@Ng)7fHHHRCgC{43K$ex5 z<83OlmrBy60Q`hK(u?TLes^#iyiWesI$5oD8idhA^xL1WQT4q-=Yyn6?r{>|pU3O1E>rF`r04i?J<+kL5AhTGL0bGND9(HV<-rUnFUWw>n>+Nk?at1F*-_9y%C-{! z;_k&otP)?Nsx3Y@AFm5kI{)bFa%=a30F=p~rBIIDKferR1@!(*C`x$(>6g#wgG>$h zN;dGyM09rMN3z-^@QmbHYV=zzc?8nTV^?A7k;^dUNIYIO9li{kEx@~ zAGFGuqw{uoheUm<9ePZ=z8T5_>iyCWHJLKO7k?TaVPCJ26knkZn9|(7e0B zl=hqC&2tYXj_#=3?Y31^NPvLwVINeD6avta8LhTZ@Jp(PR`BD-bmJg6>dTsNf8!!3 zOnK}uiU8A+=P(dK{Vp1%P`;1XKaR(8^mmQoSh$O0Vd#ykK$kg?pRMG zP&`qQLRn7J00;j5hp*1@g2aak5iQUXkEHzE>j?$djEU8+d74FZkiW zR+O$x2Ow*vE7j#(g=u9eH<96M!T{%x+tCsGroj@>LZn!iN!$Wu`6<@KvEOe2sr&mZ zGs^%+4vYcH1)Kv2(XHf61T2ApTOD&7#_x}X8K_!mA_#a?=PpeGUv4bue{-bqFgOt= zHh=<}7)x6c!#OcOsVEM9a&PHmJ{nK^dL(h=mBrDCYrWD!K`-sNlg)@~{~5=8sTZR{ znG!;lG`bjx_xVXs_V{E7a6d-MgY|-Df_$=EQ@SU?Qs12R()~{#FP+rn_M9>1JBJtya7lf#4dj@q8L!M=ncNT6lD^V_P_l2FOZc?M;Vjzd|(SHR!=xq#8%0Ln8T@bv(T>bNX%8^ zI?)8~KGrY=_3G1t)}D$zYyMQ{)=FlEj~)Lw=x(Y(93DyNTm>^{rcJN)hlnz7^1d@&>o&xn{HLKHPIbI8|zcttprVWm(@r>9bfUy}J)V*%}HM zJ(JqKu?Ed@O zFz-YHOW?N4_m#@@k^xU$uRi!;sD%kHoq2F#FH{^DB75(NB~hOB^(RnT`mYH?+ZNEV zcnWCA{vB^s8SZ~IAA-;Cxdt4Tew0~O+K)(OII<;`9Ul2MBIk#&Wo%+#7$AACyB{3R zm^2?`=44s`Iu1+x(=-eb;y6^e_CkRyHjU`=vouzv1mYbVG$VPErX=uwy1%UkO|+k= zPXW~RGa~|`{cJlhY$FTqCANqkNQ>746ipcf`A=>c=D3(F(qh1Pj|7AP%iWe;Pcb`e zJ3`q{wFIt0b(!XA&uYh2&wlpy)2`!#ygZRWx2JjJ&Qn&CI};un&`2^JV6tKT#XG24 zFY!u@BX#aFLTtD<<2lHm@-n!=*ko5KHrI*mVq+RIeN4Fz59#tt| z2zWYO&EhZ&`w{63S3X3wl6^I0bzWYQa%Qe?#C++WQ_q$rr=2kG6{$33bw0k6U42XX zi0S(izheLo-26@f>;g|o;+FV(T;}wH7jD%l_xkx>T5bWTrA-nXdr{kt7#Kh~&}2&+ zi>L*hsc>)cxEtqtHiYy`CmaTZ9jMPR*8$VXv%zsx*IyzJcs5gQfY3=Pjav+5w{{o+ z%P1_c%A2yTXK(vna#o6-_|&$MirthtB~U2F+kEMsv5Pi1WDCjjBmgg=x}`kvI+f%( zs`UEvG>QYg|CUaiOi`)oP?J^2rA~F4=vzwvFNst8zka-JxdnzW!e)wQhD&vNnW4!B z&Q^^-3TXKEI_+el4O|AL_YQ=Ve}7-R;J)u(J*Q@8hS)#?Xx8y796p@B|J(wPz(xtZ z`GoT*m?wwFtwQBl2lDCpLpAS3fCL9y8dZp)3kYxmqL7h9I>Yg3-5enIT`@`x$O~qO zN;J=Z4LDZ0_tk$NNq_KKf-z#rBelLt^+Hr{n0lcgddbOu)TjV?=+^fz6XQKnmE?625pX1($L>CJq|TN=0xwlLFE9Q6_Mktu zwfRpZ!UVEsDECB4^=PX>lBkMq>d-uz$PB!YA4ONyuZ8JUJ&S!bDdAQ?#P^ zCq@FPg2Wgur1ibz)Ad*71!+^t0={h3c||T8@ncN47qx9B1r?LSw)0m}=p-pky$hvT z-zt8EPDevj0c~_YJk!pSu@Ih8?COpXyDHBzxflF7%Ccg7!LuzjBQD0u( zZ^6-cvj68*nZ{|+WeGe6Q3?p!_G*k2vhAU&ToU--<*#xUl?QwU4s$Ffz6T_ApBZNG zUWR(w`K$As^pcFnP?nbzn*U>U|5BN7X(rtsgAZ-LQmqC4Ovug4gd7Al53fNCKZ3bw zDJ6wDU5ks0>1UM%IAgX13%YuwPrwztIE=6*j0*CzC4o|y$aT=Bv^3{~@>ki*vBLoe zKx@YTD3JEO2S7>>VOE%#Zx|=(PCKQUv?KDKHbT;Wgx=T3A0;=K+=e=5bHD6W<^<3` zmrT62`0PlP^ZdSDA(wi!8(>kj3t9KJq}pY$&Z~cX8_b-Aoy;aR;`Z`nFrf?x>z;=x z=d|U?cY*1ySC;)uZzKH(Xr{hLD+ImaM7a9)811&~e4hd?=zCsKm^oZaobZty6c2xm z^CGy&GbnKFxKK?1O7M_Tr@D}BSmi2U+xJ9&TUMCW31Wk%3pl&1yw~Z&JJe3mWr3__;C!xb7DvDWO_P91R|ykr zu&IpsV%G+k|8zn7SiJyad#Eg_Dm{W+BQ|GA===QcxPP?W5#zC#;uIeGz%7 zXSd1qt@1=XJGnFXS5#E=-=LzfMny2US7|VF4uW+Y8F=GuVRojY?g$tyC0ded@>xY#4GNi#NS;++fMlt(>dFN0y3x|nkT#KiRZ>v$ ze@X(GwcowY>4GXS61}`-6~T->r6?U0ud?r;T0_dbgCJyAyvoYel#gJ1U-2sQW@#|% zF4jAYXw9|= zNE&Gl`$uf;Hthb`ix>9zO#61eeun|anv@*->gt}Ux5E|XrBDzF5g{NTP-LXVze7Mk2L6@3;bH%7O>ml3{_bGRMS-Fa z5VbK#Z-$@#9+MkOe+NQ9cv3?^_y@(Yx1pXQ< zOf_W8fItZPzcM@o3?wWB^j`@Q0_yJu^S@=tzcTH=^`Vv!F#p0pKnVTaLqI_1LjD_> z3-v!%$iQ6a|B)fQ{{ech5^_U8KTF2v4zjUz0=NrO{0jm2EB~Wrp&}% z7B*&9mj4^g)ZOy`LHoz@U$lSK^XnmRfCO-qPP;9mj$f4YCC_h0z$ zrcQP?F8@@aYHR5%#Qxuu|E2!lECokP)4y%`hxUJ>{HN}J;}tFJobCReB1cPO8Cz#l z$G<85H~W7A{=X6b;p?Be0g9IHrZyVlmLOBxe^trB%Pqk2f3^H?rkI_Loujh7p|R;d zN&k!TAFBV2{#P8$f5%~C=lst&|6%!WrU1)7bMPN?@n1vtFYVu{6G9YV`9BjZgcz>; zln((R0wE(Vs^SiL)(O|4Cy^#B^jOUEl~4vA1`1&YhLILA!n!va1g@xPPj0L-(#EsL zQST1~>pI}zoEou|hEb?tQ+8sJBjhDOeg0Co{`%^h!+tyFG3j;b#RB=}_rX8xbwIaS zGM355QPOz{JpMEC=f3;j-)gVi%j%!^L5m%()eSyp7iD&@wnL!FfF4ekdz6V|BDsVY zSh&HW56)EbIriA@A5gIQA*@Fvc9P%2MY~~Mi3bG^G^PiFDG+ZWxfXj;WTD7o^Ra}U z=)YQ%eJbWz?V!vsn@)D-P~%Bk`hoI>T1;F-yYi;Q;BkkiQ=A`@ch~LmxE1A=*0&I`G7Pjv^gxl}g}uif)DBj=x>q0K~en_SZNrODp( zp95{D?28pHkmv(s8J9&MD&uyNirI=`a^)J5!OI5d$f&5cLAF?~(^gCC>9DVAsK}^Q zCzqG)4ljPbwrQGzN2YRCstU*BKw~9-MvK50H zHL|%M+lHysD(!q*0*MeAj#o+0i(VwgfF`i~P7Cuw=MC_xKSUuw5*|36BTsI zq-o4a%j$5;DV%7lBTW-|p32cNHP&D*9|_IAnH*zHBr=oaU!@cixyx#2Q+}9(nI_0z zTQ(Eb`~}d4ilBXb4kw6H{q!~wXr#gL9^~If;amUoqUqf(3>vF?f2Mvg&(p@WT+H=y zMC;dMXrMmjJ+K69TGkaH>Stng8qkiifLh5IWgWc!llySTbshN3J>dG=MtB$nLebk?&R z!Mphzrdhr>CK)u3)M_BWn_0(eWUVf)Bl?VjPhh4ZT~=MDb`j04hSCxBWhl85W?K#N zlCM3LRV_{|qEDoRM@FQ@$i82%o(+)9{>yvIUCR#Og(v4ggCV7V#fMETHi40ik!5($ z@~5iG=ocCXMQ{45tK2|8nMv;a;k=%ozj1men;${KJ`V^ve~PTgQbB)P;s=Gs%3lZVx46cOz$mf}%AnuOxDnU|=(M15H+ zm(7rXM0PZiawv}eZEu`9!}aD|9pZpSC*7`0&=6Eb`Xg%QQwLy7AzMpf^oZ_uLF^pZLT17gTxvh+DxV=_PTY1?QZD*&5 zg1Lz_7d|35bCKE zW=Sy>UiV~A9b_vuu1fj@70*IoQQuaL2BJ#^!fFqGpJ2zf`%pZNwlbR6S$mR{8;h20YTYQ#~hYGd!2jXDi? zZTlpZWnisCvg(p|p#dMr+z{{`SNLn^GaI#|O@5ce zLB7%CmaTbXkOthJb1YD8+@~>6dSkFdB@6zv-?R)7dJ1!<+Q8LxbJ&>L)rAy3D4myG z8;d`&F>&jw@vu$nr@O_Mfx?&xf$hXm;mJ(=^aM^G{Z`fg*V=EbbSxlxD z?d@fCSQ8xrbF^nq_F^V|nKe=cJPC#!wtp_u^O3SC9M1xG6yt~RRd=L@Tp^Zy(&c>_ z?gFq9Ht3RJIf~^>4oP)Bi>gw)HPg977I2T@g!yN{C${z|?*L1jw6Ms@VK zY@}f%Tv$fHAET--(*{lvL%O?kd_rcu}%-aiVXo1#-T&Q!hy@;w}jZ30p+SY1-vd;erX>S04olfMSq5#i6j=eK&_;-srh!K zq(`6l%Kdi+wefLDf=0BDQ33&*Hy5QC<3{4<2D>3Hy!)JYUdfxQB{eWo3PSe{9p|`2 zYKZ^_Z8V{ACmFlGPHcQWfDGDp^)V%&i>*5Ibwfe#tmXClWer0h)9A*11e@h+68@UP zR50iDNp7556s**Q6O{mc=2sqO(+0X~x|cx62SVJxM?vjQ?AG3MTnb892oxEEWbs{gw0g9Y@tlG5DZVU85iIk;DB zqb|l{v;jCqxGE9slU9ri9R(x1PU~WEV+(7w=Xpmo2w~Zn#kvAHO|egI+9@HWZBx0 z0#OZ6zQQ)^HxyK%%EF9Ws;q(*yIyZKK>BGJVV$Mx+dyu;WbTo#PK4~!;gMx9m8sZW zjAbFCFStUlU_gPBX;;4jF2yH2jVuoX@x2%BE-4a{PZr_B0*c~z5IAVmrSxs%+@nw! z=Rv6^%)m&7Issw`3b5Gd5h`C6r~R`29AB~0;US6=>2Yz4e3qOd#le6u$hBY12HwG2 zZ)knv_4l(s&iDOBvcb7V*Uq* zjT1jP~1`>~#nBq9OWF@O7ZYv^v0w!ft+ z#%zOs02_#y1TGQNO&Y{% z2N6k6S3ag!6f{Mz)5=tK7voB8?x!su=I5@zX{7GUk4x=PKphW0?RG$IX1SpEn8m{1 z3oj{>1DeB(1yIV0RO3z0lW+?CfFy+ZtHo^6R?C znJ5couGqRO`c_~_3UHF)I9WvJMpW(951AuH0h+J6?P-1mEiieQ_%XHF*tSKEN8ske z2~S4|wT2B3;;efsk~+sLY!yuC?i03T{#w0LG|xD*!2j(7s{xL|RbsWc{=8BwaK-Ig zaT~Bz8Ph1G+L|Sh>n8WT> zck9T2!a5Ij4Oonha``#STNMK+;=h3x<##Tcp0}luubE+fnU4WpkP0*`uPP)>)*|*c zj-K^&^&r{4O!|3gaG%9p5wuFV%Ra7M<>|gQ$MBKKj1|UpxhU~;6YYWnnBfWYZ<$b* zMnvd@`nrH-S&8p%SCOK^jMF){mCGiJX*xy(#GL(affeEFrL!)UN)j+FNLH-!zd``;XrcNleih2(B6;w`t2PO$+%Iy z-BsUG=g))l2Giij>IY#_^LZFl5X&OM=QuiZx1A@DcLAhu#w;6|`_^0Mc&SWab29I7^RO8vB~a)I zE7_JQf6>^Sl+d#wh8Et1eSMkKb$QFc3o}H^z+i%Tj6UTj3P01M8BaEbnuuaZ2$LTI zJR=R@LBA#1>EIVS%)@-kc?y$zU+g$GmGXc~<_&C=pkWc>`?aE0i=r958mrwgB3VP) z9zerDg|$VPYUtslv`uUM64@f{C6H1Z-NfvsGbf>0xIS=NT4C8B)(5iF^ zcr;}vc&{q3DV+%cvzIiwR`p%%v=g^NDpm$fMXxcpA;K3EF9U)6`rDxqY>8FtE{{U^ z8|tk)C6{45fC#{Yxr$`lvS8zccAoA`@hw#;VIy^kU0k%ZhL3>`~6f;vTOnH!wtd!;ma^JCH%h)Rk}=1?nN|9JZ9kH24Rx^qSww;n(!5-Q;zEAldy?{k}_9{+(quc@DUpPEH3g z!}SnB9&F|l3ebb+9*+4}zA9~!?dUwlnP|&|R^|oB6ttF7H^5G;H-QCeY+Hds+ZKJn z82Lc#dwjspzN@@Slsvi}S~8?*5Ee`pINgQSdc5a4q#}I@m&mZXu(g1{8}v0A zbEpDv^dr(Wu9*D_(3|QItByHxZ3!xXEMk8>n|*hwSYPl4`whZoCFD(%!D#X?-yN2^ zdR!CTqX2@)-I9pC4)~kNbd&c{@181E51b;bA{4R=NYra0+*%i+e>&r8o08@(ewMLu{9?-`h@#1@_qy3$ z^3g?v-Pbv!SbD#&C6iN^FQqJ}Zjzq8m3v!-t-l=e63w1Rm8>Cab4|DXRgMPb)S^tg z){MONnC(XXTcN8iw?pOpfs`NpiVHy{s*_xsD}S#EZt#?5tn!$6#m3b#;|+5kGv18H zL0*}v_Ha~hZ{jw@cuTQv!lXz{Ss+yXHVst4C(I+_MN>VjqdP?|=OjO))hqhhIK;M5 zQBshP%4+~q8TfKX_!Y~vME@LV2ORY^X_77drQ7YUB(+U=e8i2iHIOGueI0wAHwx9j zfcsh3`vp^Tdf$O`?_`Eml(|eKO)NfF$f)hOzM~04|AO*W40)1kNFdoFDCz|r9PNGc z2-8Q#YCG8Qqw2iQZaU@ti^huq8&7&+M4j9n0Gtks2 z@~HWC$+d^vv#j&G=}1KsT?j(emzFM+Mc0Mtq76my_zQ-Z(lle0n{I;L&M<#dne~cm zFW0$ezehEJ-fxLjkwb}G0&vG}dZS?{K~kA<0qY19$$5|lQvpPS@$HVNN?)*hpKE_> z)T=mCJ~^H`F#Bstz}Pc<%S>yzKQV_2aI?RTgb$ng#4``$bFat~*|rE`x`djUL6+`x z3Z)XpNCGo{Nxi&EdG*e)!vrxo3clgcYRYh*1Y-j}7Vw_mtCf zvPQgLfdyHp>ap-1ljG{Ow^K(O1f^ z?rS!%6mVYAW3=!v*GMg{%u1S6(ifYgr_pc8I(3D2{-es|DkVkH6Pnb|K3(mBrmsJA zFy0@fqd2cMalhpUkJ3ZZJ7=w2Z%^lG184HM>Jf%!) zD}^JWCOF~jhoxSD&}Ri^4xGB?XwKLT#6C^OxFw9vmGAvzYZ+fZjx7&ywGf#inyT=Y zbC2Dskf5Qc<kM#0hX6u+X`%m$)-jAd(QlwXLnW{la9Enm7fMfJt_NCyfzz z2$$`EZtsA>THMOrj%Xn|uy-NJ=J!!3=B%ZB zn}{P0qYUppv?4HX0sj`d;($BmcSuvmf~m;D9ppXsMy&cZnsN~Tz&y>A<6HYrn8l|$ zl#aW!oz$R#@Y>FlK!vAdOWM6s*Si7Yo0z1gSKC_RuFSjDNH%t(!ZogZvr=vufD==*n};@$ie-=QP82Hyc}22 z+6^#^d!zVi#D-aW%&rEnv>GCB)m7(|wXEuEaqI;&hjfN9HhjnlMNJqF>c*7;93lChKmi<6ey!{Pt_<&h+>orN|KqVvXS zx>vwB1Kbq4S+yCHr@qXyzu+(!lblJ=nM;ZGocf*aH##2!ubM9ZR+^Ik=uXeVMvS+Q z0=Og_VJL>kFp6ZsNCL7&8BgAqCMEu4k1i;^DG@-KqrPHBCrbEcY7mDcjRv_(l{f{F zvHKzmDa#dvuthb>t z{^9xYcrB(N^uQVN64F(DiQ$#)KJ#g>vq2AIi+e2mqHkRmB5hn`7}5f|C&2)>!+(gW z@JBJoK3v9+-V{a%j=Tgjd*7>IUTyk7!amX3__Jw-L9WmcJyVl_h9fiSytPb`jrduc zU|kH*>J8NN%aeV4g27@>G+>qa$`Mr)~| zvubWnpnB^i_&!sy?PX{H6wbU+!fh~f1qWpPS95(^WfivYDJE2l1z0!X$nDq_Wt?AP z^Vm~Tcbcixbx8Vr7X68QGPrJT&B6@@1nr_=3gtzYs@`)I2%2R(r#Z=()* zoFEQ0gFSCKTA+zGa%%?j0w*KYYXR-6Gunmrb%e`mDcHI1#X&+d>be5*F~sBe%ap=M zhlV)n-3yU0m$m&VuMiRZMF&D9lSIaz>=6646O>9T&*0NY@l&|aT<5^FElkv(PeB#Y zLtq6m)KVr`(Ks`&k-kbHiDF1=-5;Q(aArHLGo3;FC+TbV1vJ#*+Ik4_p0=_fn4J$~ zmdarY(232WA(ALVwg^M-2~*W>ZD@zp0OXO(jWpP>|v<*_xD(@+4x5t=pTC$gk0O-fDDjn84G#f_6 zG%T5V8i%uk&;>tx^weO?sD8I*rr*sGQQLTf(62&VllM7Bv}-U$45cNNYV|yI=uqRG zwPPYk4u?Qur#ueXE0wihwA{Q~+v8{AOt3{kdLW6-wRXxdUDwjuE0W1A=lxW0k-y=x zev%2{4WJ&nAq@jeVG0l31k0`dea47GbLP-f>_>Lg&Z7~}u-z*6S{H3jA1HQ`MxFcY zKVYhAKKP~1g~#TiI0QTudnWcB42!HEoQwuRK?5$(rWSRY-a{$PGyHPN*RFxor)C0;= z3tlCFgP!mopK1P?qtEV@Hp-9gR>gadnmVE8?;dA6>b<`(^E;X$T3T6NJB8@7sjeVs z!v3sd?QA6Z0;+4gxiH^>m-|cCmUA=1r7sM*Q1~2_W>b*3W8pyUkK`;JL$LfUClR<> zhYj|~<3&cUTmVQMiVsDzq&$GHLn=N$N4YaC+LlJYV6UEd^r(Ww(BJDVey;DZ zM?mWu_LZ6@@N-AT;o?iU%9$X2t{Dyt%#-U+Qw$(e&+m&XB)*wakA1*J>pN*5b*w=`B^ zC{=-Gmyf;{mI6yM8`^RFM-pJ^grhp==~j>lNAXa|YKsC4fT~OBx7u&5|$~-$Q&s(E-=0pinwWk=cyIDE;i<+yO^~`%(7junr?N`nG;W&<}2Q& z_>wNU0@A8Qmu+sOl^`WS5p}y|3l1+AsG?aO_iv#tPClN-V^gYbumakuQb8+UV{*L& zKvWuF!xKVfakeeh0Rg=#aA|U-2YmzIvO_H>HrzX`;H$rke`YWZl&bRg>Tr26a2 zsqLZL{U)TRH})?WUfD$X6&S#wZ4W)WD5k8PgxZ?$#NmDNNBZrK)%WOfbaWN1ZEWh_ zC0bh&6%%jHCcb^#q&P$F6_0+9)6?biO@U(~OK4=D*eYXxkgYWdMv?GPM`?!rN}%j4 z0n`p_pkwl%h-|h~fqq$Ly*y+eb%zrYy^xdmz;ua81)Nh@H@_U2k?rkppKVy!0o1xorXCr zE4ql$_Q{Q(!K9QZnHF=?Ecl!OiK+U(}iuf1bodS$5(L z%~Os%EDkB6Qix-WDCo;U8%;zAU@10JXf2Jz8(5b>_Qql$OG;4L(8>6Nnz@S3+<@V{ zE03&v=X~*mt{q%Hq>-?1PK4X5Ywb5DM6ccKwt14BiR<*`Rq03;d0-GX@!1_hMq>a= zteB7^C%pa&e9c*8(qyXy!5XeD(|XpPs$I~ThAZlG zUWNrdZafYKDJy6E4I61}YH+mh{!Umj11cbPB?*B~Ob$1>cGIRE$AB8!U6)7F7#sMxiyB zx87@Q^;XROQ{ELh#HAP7Vsbe7Y;<>XIt_h{>76?#qnBjx2;^%b!^@m_N}mCv zt^(RwCA?!~2n?t2o4Bb9G0=$RC->#f#to(K;Os=>4%;Dm;pl5Eflp*8+UkQVkH1>N zd;e z#aUxDEz&r3ttm&jL}ZnapZCpVy&DTd)||GZN|>gY_<7#(YFS0b{N1KN{uKTd>y-Lv zCc3z! zRZKz<65Amg$Y6v~JLqs?-#47^`MV}`+_+NbRG)hi6nNt<$@sa?IdRlsi7s$%^?1B4 zg^(VQCte>P5@he_o-GH~==-%WG4w8<#fmAHolB7~)p-(Q7vP3WjzWumqSjg6lw{Uw zW;SWz;!ai=EUnH68|``;{-z#+CV&u(E#OA(I;KoEbU&u{aY0 z$oA2>wj(glkO{H1yP&JIaTj@}$4Vum6Q%^M>4DP5>Mn=UfV8xO$+*#;qjR6YT8ZbE z64#S&MO?bCj^f$4w6RYMMY()<4bMpe;?;VxdS<5+MGGA#O_C7zMNvvv??$9v6fTYu zHQvuD)W46Uxm57i;i-Z@y3p=V(zSaITI*m ziBlopk6F^%%;7zmt$J70Kdeh@;Y#Nf?xPL`42;D`nMzU-p6pj%E3bCQq&$7AW&qC| z(X0Mr{ifpp=r3MY+Te^vdO@>_y3aO7867=zw#l-%?Bh4&w?Z`tT^8PZ(|mz=PRDi%9o5GCar61p}PpZUFaxa3(#R}JyoD(18i!T(G zmgkLr$VaOqoH-x6&F_4HY}pcvr<||3X>o#Ews@kb=iOo=$w9gjtBJa=$ClM5WI^TOP+mW(v#{Kt$dK-Tb&c4q|y7rvlP+#8u1bJ06Vm0 z&w_zQz?QUOpRMeFIQ^OXBE~Yeny@Y(MTG@Fz)O*^0q3OsINiOsEgU$sk6%!MS?#Wx zM&{z_vZb}?!I;8Pu>)QiVi*ApKjRx?H-otgPcdWOw6JQNKlRFMH%<+q2-@5_79Gir(g&ey&m(fuVRz@0xg=MioE zPT$q^dj4F0aST#tJ3WMAcbI??9liGilLDZMsLCvtT9RtNo2yeLnkvL6dS+;ReclsF ziM9CXGtVpttQ~-m3#1mpo@u*(aMDNBMi+VTAgURefdJJRX-myr(`s?t?hZ3q2^959 zrbGH09YHiaDakB9dYa6rvDWPmJfH~hE?D_H@}`^L-F zP?bwu;-Gt>X>F21&mANBFqqoaUx&UWdaW1SfA{UMlz;JA{?_nx9LvkU@D=V|4YMd4 zR^WS%3t?WSM+OsYZnWr%2X2>g8s#760NmvwN4uPaXA4B_Pue;-Pdoivo)xfcWdll+ zegJH)I#jAj1c1zvdqG;%j9{1*)RS*!)OP+IuJN^4a48BGb{mWPvMRC(K_f#}K z?uoI(DlNxKOc$=|B}L3?hJ zM>yi%GJQ>Y&Vou~gxR?kd+q05Lb~*f+TsY!x2imxB3>XIzKP|IFb2`}?Zf2O-=8R7 zQx#t;wryCqUC!$_(OuO4qPTg$L!et}TE2DGIkaEScpdr5I<@)BO?b*G7U4zIfv2)D z!DI(hby}%_xXGY=Rbc7}u@KO;{JVjK%(6{Yp*Sgg*|gM!D^CQ8#-X)yH!yDD!`q)r zSxV1=oky##Q75xF;6VP{cnX&(Ji}|qj$~SUDo8kjJ(W$okSKPxw1>Kt?J7N{e244gOk9A9F}ZxUQ)bG&FiEjM zhXfw)L0`0%K zPA~DX(QHVykL$pcMi-acgsiSf5lBK!)G0@z%*c>G_F(iYEANu9{+|y#L&Y$Cr!*Q6 znk1j)97#glvfF%`Lh50?p22++5=iqX1*bMziX>%HsL~ueoD}QHM+@GatGgejnoNe* z);o+1%CcIJn*2!c{1e;P2|3!C7pSU{aP?m{nFk;r3%#MF@6HiLNu!7(0K~xJ36JV= z*vbd5yXh#iu7=AzjrztYs{{$LbhPtd9MHYyhqjw76xf|7E%%+8E4!|0hXn1PCtKV9 zE)g657_P*sjH;@UsI(0jKDW<$g}7`ZAGtM+MPR;_oJt-%@S!8ksk%}a_^fN@(XUZR zj#OBZSImTX%tCe?0=p#UF)o)GeMC`U$CO?DOCD(16X&R(9utGn-^av6a!Se>eWpjO zcrwlwwreS}t>tOxfCg+EmeNs{$}S40V^AzQZH?FWM5RK zWNaS=^h|kb_a(3s9R*`r)G}G-S+kY9Y&3)?YhaY3<%pyuy+rsz9QgG(Q5=es_ZWqd zTC6&))UKV_#O5d73NGFF z>}=0$vX$|1O3XU)38`#(XnL1@I883Aos9CNV&T-GVnRpX=mbN?E(;)gf0XDyp2~bi zy%@WrzFG*&?gBsDL{Uzq?8u&}=0S8HN1Z$DHqxL#-?}fAmQqZXsiu(hBiF9RuiPyz zuz+j4d(}dD!bBQaED(;CMuNR@o(=$s=9$A#L$1k#>J(0EY|y%vASK|0_!7Qko8R6X z#PX6(OTv-iL1-gy#Uu##MiR`X3`F%naX{*Fntql2`)kd|ZpC@_WG*JBVQOZG?tlhl z=t~>$(46CZ6b=dbgnr7jD7EM_^f^ib^}RF=sds18&XT`M?+JJWYKdNrMsrHWzkqA` z9AWySw9tY>&H*~qRPUVQ^hKkYPGTW!H3Z4G)C}ue_(Lucz!W9H#g=1crGWcfCtAy> zCEKy+KA5~-B~d5C{Crx<`BQ?g!C+)R1b#2tAk+XQKg_HCV4uheqNhkS8b`5k>|P(& z$pm{t(-w+)55qvN!~&C995mP)NdYgDSMzqs{HKI8tZ?Lc)%}P{;a+|rb zmfNfD@e5e3@Kk**`(eY!74S6GIh)+*7Fuiq%qo zY}Yy@UaW3{5iAi8Lb9lV(h1Z?WCj?d-E1wWPQOEd8TX4Q0xrOBO|5j8WJC!A3lixk zh8}GQxgrsdE1`obIJI0|3rL8)Vz_|;LIU&?7Bu+*qm{{hI=Hj`$N;OmCfPX|Y5K2b z2b7}}F=TxkZA|9^##-sDUI3d?4nga`%aHg5&!clfj}A3&;YUr*_BcUWD=j<8&bv(3 z`%d@w&4r`Yt#nNKE>dj-;O;1%YG4K1b-lnzuHdZ4uRkJ$p$IAF`eQN^Vuivcl;`HF zMPfy|a@b_0)_ja`O(hNjqZ#0b?TqQ;CKtwKaa)lv15)dd@A>&ul? zZhN++uinDZ&kmO6jJUUd-|YjtWyGQKyEi*81+*9Ud+lV*fDem=?zG!nd}K4y!BE;g z-W}D9JRk`w_29;Ev+s!|Ny){eGwwcTHN-J5jEv$Ll@>|tkz!QBhQaL3vS87PUQ<1> zijqunS|^X@^o?{t7$Y}VdV=ANiT)XTv23;{L!$B6RY#*AY}Z8Zbu^Yg7k*F*b0_ut z@eR&#{;91z&(AJ~pFJ8Hc35~nOSbwyC!XquYw-r;m-J2fU7S<1 zqWVoO{^avH-aGWzRY`9GPG|1GPSrAadLH#_7X;K%YWE*tHyH(wyZpPzUc8bL(pugk z5Bw*$9og*#{(CU}M!=Am%pQg1sN_ll$)V3Sz>4BrdPt5DGcPE22ZO70O)S(#1>ZVz z$y2P8x$IO4V{Zm)!fSji({=%v72_XvS~v~i-bkZh7LNJhYrUEgWG{O;a~S>8O` zP4%VMl;NtL?#4|FniUzL&m!WwyDpNwgpDAOve%qG5HL`MFw@cX3x@IqC=2niUfGpkHpW>^D$gM9^Vfo zV^-+x=5B_EB`y7qEIhooOAH)jwi*5Ecl*L%pXQ#rga!1B$ zS-f!|k6s;x&)(f;@vQH|p)-x$?Rj_IZNuZk=P)hK5d**_P!LAoXX|nD> z#T0c)sM{D4t{4q!hWgxq(1ubG$z}xcd9NW(C)(w@zTTogc@34#?%l^? zJ%IAYy*9gvAS}rT+Q@XimQXKIc;8z*Ci?YJpLveeRxqNYV<_G0cuS6D=Ajf+jKz}QOjbI8Ieb9?f^tq%Vd@1?zr_#hCHvJ&$jmA#^w3( zV8H!CiXeCAX;O@W^5X!`P~EEie!*iDmEB(P4-ZcOdGLVQXww{zHDcAz5>ZPC4KIW5Cak0d0 zNy!hS8vC0QShUmDTp4Ks@o`Z-$>am2yRU}VKn{n~Pmil`=%>Z=L^D=vR+>(aX07u) zSj~rK7o2=dsa#AzJ%0|Ft}p)H_V3lrD(Ogpa(6bFmo70eZNJNje>P={L{&_(b$n`= zO}uNZ&d%C+7$)0UfWhr}Z6$VD`Z(78^Ky2#L?g(US3g_a&b{@TXh6gKpCRMJl(gSSW9*>aL(9aGCk;ZqxOcJ&4|~RRV**>~KG$!p<*vu>;|qz~3Y!1trw^t{-=&`8 z^GSCt&$W-&5+b{HXt8@e0sny@^U;K9CqnWslnO}qu^sqC0~TtG%O$K7BSda2ff0Eu zWZ)^(hv%>M(YhGe&i~Rs~N+mjvAV_kDJuv8f(b23I+u3j%8iuS+ue(smz@ zF@sZR^qr?`1jiwpIgAV+l4})7CO?I9~(7-G%SCg{RCNLW1XH3i68^EiaIu zL2o&K{f};>8ReWmLy1-WM*gAE59~jh0+#obicsQxxIkaET=$ud&u-O6G8kND+NxE| zvLHgt(?iK>tN};4p8y_MB3!oN_t$cEZgOXCwsPiKUOq1m)m8H5OuBKlTO3izGcnru za9hiRn{3An=(WhM{}7eH=(+ z+eYd0=P{*_?9cX0!(Dlj2K>bU6QfQNBoB%CBcAU=OT!>D$1I`1X>4lj>mJ&76YW~~ zGj2QX30>Hk+s)hKyM&??Pii!c_v}MKp3d8&!y%5))r$@!bzbj^>E9gY@dFiWKj>pT zcaRC;V>R(@0`=qdu(d8}7WWC&Vd}(Bjp-x4!F*?T>jlE|8c?pcq7Yy0)|j&p43_xi*c1~llh0eRfV}>Bk4o?RmnWvag>%y2 zN?iKmDoaD!*vx1D?m3QGqiu2GxE?zbS66tz`<#FMSo$#@20s|({a^Z7I{)(J>1U_B zB~4l|TV9JUi-C;V*0jdWn<=-izVMSjwd&i1k3Dcvde(Ce$6HLB<+$c<;(ncix1z>j z;{L|B&Q8bw&ku1$i2d18_?C|BTIpv!x)f#$E0}HYHy2j|Tm&BTrW4Zz*WJ+K>HZhJ z`};pg7p%M?ee<=iOLKSb$K$y>ab-{z**ru+C|&n;)-zS8s!89{uP{T%Oocc$23^on zbEcKcUOvpi-z3nFy;nbgnjbC{I=!*u{R5rg^$T6Z;5;W_K|^2cT}Qh@%eQ*zKg&!f zou_n5-Vdtim*-xIv-1sE0ssI&07*naRJqBxb;*7~jyZR>gR&y$^XDlzBigcNV>;sK z_dY0#K|0CIfiM0-T54rnTs+@2&@OoScJXni>r*!6@uS!R}FDc-;&77Ulw<9RK&cZD) zoCtW~?VU+{-F4oq^w#%&F}?UbU+lUF&}J&0qwob-PKvUdWsK@PL_K%M9l?`Rl2mNy;<-z^eRX*{h zpQcUtUcuC0|EPs$jYd(Ta)SKXBk`G?P^ubp|{ABk~e)aCA_9R77x#LqGd|nwjGt8 z_6sMfak!$Iuy9^F{=~1OlmB-lSDN0poW!)q#D^7P#)7%&!(aMd`uPP{?5KqAlb>H* zc}05F7f($KX3oaD2t2-!bH1X{Ntk`Cb89{C6^ z_8yLRv|sU}qtZQiaJgNs2&_M(eWcMAqd9?2*lR)h+=ot2-~ZJ`e>Lr3B1gaN>*=2B zR^zor+^?6TN*4hgSY;ox4U~E^R6X%^ zU%@ko&8d$MfO!UvQ^P*KhmUu_kAD4W>9igEHrBm(#&N{!KbP*gc^z(Zq2Ksslb`vs z4}EsZ4f1|N4PF0?ozg9rtxV5)@7FWpuYvgbITxkVzxdNMZm(Ta9~P?}U1GP$EulT; zrE|_WH=X$Pvw9At<5J=3Pv;#@igWRv|I($GrFUQ<_-iVs{r-39n}7OaS~71}c~+ur z@cA;<93IZ=Fk%~v%ym{{KIYuqRo_+D?wfshrHkeS8x~45i~i*}jn-L7DH=UcKAyn~ZN{_HRkg{>RUy>+WcO7~H$Z zPUN!N)~C09;3w%LC;wwgcWu(QUvM>W%v-;ae)N~+od!EVvKIUBwIBFS`pRctm-=vp zSNhGIb77FusChDlNwyznhmU#4?&*(LuQJ?UNAc70>5onm@Lh(@`MV2jgO7zB zljmf9TN*D}yzB#5#79whl(BeLJjVE3U^2cbyk!0^Y1u8ekHYKyXYwE4`eu640SDoc z5dOCM7CrtY_t3T8Cd~p0{Lj;2NX^?^S|6zZG_v5MPWvxPxb)k@;_@&f0_ceAPRB&R zL@BKzRZLNum^(~Tusrl(uzQ9U^70T_`P**2IX(5LBh%iC7a>~gE^~?&?1V}CR_y%O zAM?Jn2@jP8QVZce)JU5FJh7``&Y~ z0I}az^L54@aOU>uA1+PDzv78${^Gf58@?c?yM|lqUrDs{1fiGw`O60Qs%!f6>rPF- zy8NbIOh$$I?=vn;Z~ePROB?u;48Qu$U(&~adjDR1ef4)&q+@?|Y1;4655X&#Ykd$S zf9=6idy^pNFu{JCi&sX^{pRuMnD=}oef)=`e|>lc?$h(Pur}ZU?8Xs}d(SG?Yw?Qb zXTJW6^v3_dqKfax=o=hFG{-}<>W$_-{)8*2iMOvyWAJU~FaOVv(yBY|?9I4BY5)4@ z)VeR-v2K0ZXTiL5<(EE~{{Fq6PT&2*`IXg3nAtl`lP`tcy=haPxJP2&#NM!Gb^6Nh zf1BR;)MutEZ@v+|S+~LQ-llFmFyIwX%zk5cNg5Xh_!i)^GIPHQD> zF(gnXZ;HgGpxlK>=+^|SbYT%f<_<;D$Z8@?*ecaXPj-J0&&fZq}s*d#vzF~AuO9V|IY%`CBS z${%phjJ1KJ{liR+9y>nWiJz?4b?@EM1?PM+J?F*mNoW4<5(0WEPd@yB^iMB1A{}wS z9%&Zdap%ucti|uhT!IDVx94A*PQ{my*7G-sMp?dm#s%rv|H5m8yeiN}iYMJCBu95^ zXpi~0Pk-I5=}n*e)hILT-RuLWpO-%IiI?FO&%4vBKYdp3hW7#a@iYFMPC4=*ctB5a zSKBD&(ITw)F1;u$t_B8>V?Tz}$KLCLD7P*lBacEcj_`WHSSJ@sKr@Qv14 zxEkfJh-^#C@O^+kE?bqpan{mw;nkSLM_E35#-Gv~|K%BJJWhb>D{;1?v}&a8ugABU zZ@A!^v~b~E{07oJ>DW`x7^w@vmkdfDQ#KagPq=0EI{f6yjP#xNzb*ayr@op_`p!># zjD5ns z8WtGrf19jJTiSaz>fLXVMaoaqDC2&LW6o#Sf_GnvbDMlc5FFH}`Sn|%#Y-OU62fD* z#p3{?-gK-K9_6OYiw1ezZ67y5-t@S8LwrpWI)UrtJafjzwBiQ57cXyegamsaw`{== zc-_7>ZNyJhY~f{_>?HbS4A#t)f5MU>A(Q+V+&>Hg9OQb1LzKAf>L-BQ?&T2gdFQ@^|9 zy0qw7|DF!TPhL#GL-t$Oj{G(uk4DZMT5CMdM!R(k7K1y}bnJ@{zvY$qJ)k-14WB-3 z=)|CX&p*B}z2O;0rJ1v5r;YgSApYqB|MPXjVs`{!Ene}w z0gJ&$emweH0wc}iHcZABU%4tB{Fwc5T8>3Jl=`ZG3hO_13=6(wv>o3f?#J&6z3?BN zl+K$wD;@pz&!k)Py?KRkI}vZ#eE&nQNl$zJktyLzSKBUMA&UuhdIblc*l&Y3-jT-R zi&roC=VzoRJ!0Q<_{%?#mg82;NXu{V-GU|fZ6bW5OHXPl2bO;&>y6>`dEQ0KdIJxG z;;8gK6J;v(pRT`*A!&=zPvN4ZlD1z2n}Rp8VKHr`vD3 zC0)5}x!7~ab0J{820u`@4aeBdlcwPIjNj^3Vfqv(CdajXMOIqSEq;X zxftJtX;VkG)hXNZs&Waj z6hrD5URjH|;H*Q`j-e!}5t=_lTw_QcgeEYl}X zPUn8)ed)=MdStr#n(NX!EFSXx4%pKd{dmnUgWZMaf|p#gEIsa^L(-RD|HkO5XL{uw zcfhC5l_f^Z-D$5q59e8T%6N>@ujX80cr35gR(z2e3*+@6?Dr;U?^juEY zSj1*q<@7-F^HYWDTh)e<+#+5Y$Yk{+deHpAvyB_KrY)B&PtSkZv(u%E7p9~C<-gLk zD{n~$EZIGs{o|A69rbmqZpE!J+;*^kI`qL=yWKBtJmM$n2hzB!Z%*%c7;M}$`k6TUd5{jyw$^> zkJ!f7W6iaP?8(V}0FA{@T};9+3Xez4za79lrsItuzRbZ}my(AqfH5cjNlNYl%6Q?$ zE0AliU6~HrYtMASr#_TkdeW!T8GpV&GCTXkcclaO-YZ>k-E#QXh1Kl|pbz&gHT?Ah zciHk4>3N48o}TgWN2Z@%FtYD~ti5N0+$Pl-O?Q^Y@JJ=5BpEgZp~5ClX`3v#0I4xu z*2L>VfI%jusfBwN6*q;-zOe@sR~nd6B+N#*GnupfR&1F`@4kE<~?Q%fT-@N2pbIJ8-|3@5< z&imd;>0KZFMtb=RpNOY6E^zMyd{E)if}9AY;9Y0QilpQ_oGj`KtscuPN=?2ZuwN% zo9gU5D$Ise7CRCAi7h8G7O9LTHttQC>@rB4mMaBZTJvm776W2NSP)BYd)ZG#Xd=v` z?E|q-ybP0R^bBZUEL2Vad<0DKsX95k!$f!o&IV`UxxuGC_VzT0*WlJ%dlMD`y%JCx z#g`c1&9CkgJruUf!7?T-#IF}&2ljF9F)cT%4$1%%{o_8z#)Qv z^uS*L-L__Z8jIT$Cmi$lSSaxesd(Egn-j$1_F`lr&=DtNYyBqNBgdECMq4}{?Lo)x z8?DQx-Me3Lbb7_J9+!59RA1>rCkK#XmmuOJ_egLP1(tMrtb@Z!e zP1)rt8;i;Q#?@=_9{+w?|ytRs=Hd@mc1ySZ0k)adr#A~GsX4bcw z#L+Mt&RH^4{1}rs-WnQ{)}zl8U`&-#Ul-4@mX_R`^1{cMMQS$@IRUg3PB_*%o@DK& z#VTiR13bDlom;6da3FNjGOUvSDK z3>k9qNk7VU7wC@EyqLcZQ%dDPLL-&N7Bq2vxFy5CKsE*sJmn{M?RHRE4AqbO*H#6Y zL&RixnKVGtJ3Vh`v4ve@{F_hqOTSMFS~<%hr)_{ETS^K=!_-?Bsob7XW`p% z#@R1FzJ?sRUrZ?rh6AHfMbjTZbcP3{JL3y|JL9_*ceXr)2BNjdBEmTLW7%)FUDCHc z^oDfcVF%%lCaq0tuDcP(A6tkvBX^w(tTvvv<*Fm$GF};M!W)7s zZ(k$6`P1y8H;nq@)@`%{XwTw)NN>fhzzKNP$A025+-s43m3c;UrCntWUwZu29%F3R zRCX@3dMI5DEl5&wgmWio$2M!79NK=6P9IA615@JoDGxHcKD3ZCc*W}k>^5qj9}>$$ zR@I5D;(;~Zb#)_q=kkN_kh|tkw@scU2*!U#!}XrO^UO`#!UwIMC;LNxY)^4GCB^(S z7Pscd<8Pu)NfRbcP80F%!-*3n;dvkj8osQCHo8siOZ|$>a+0eeOsG5K8_^>#7zVk2 zIV3cH=FAv68;#uS7vJ(coRv>ZTk$}fe;+oEG4;tw%f=ig?Cm$>)lWRM{>*EhKJs8@ z@wrz&4S%~}RvN&whe5>5aWKXitKu<1406H2Rn&jK^vNTRDF=7<33DD%10I8sNTX2h z9})P5IcY}x?RrjFV{m`G4`ZSa^%&g35*c(F%k9jcv*trQF%An})0~QLv(Lo0O}%6q=T>HCpYtReytnSVl1DrsDO!sWYeJoj?qtZMYYUvy{P&oAB`X zUL3dhGk3TrIsx}gr_98cu`m(fMlHq$T8l5cZM_R`8{zGRZ8+1TR}KsJpYy>&z@Nq# zd0~&D{X8(_%tZU2F?3O&eq`luJmHNKbhD zL219;XXEe5j_j&n&s}y-PvMumuD@Lt9`!1&NlM8r(eP*K*pmp%Cr>CN~q2Hzm* zwUmh-US%ROOQSsm21ap-g@SG7M32A5qJN;CK>mmw#shwYUw)iU47mUCB|`poQa}2p zpZ&;ZEBF>Laob@dG3XvKtzuWgAI2R10Q)q))ii=4{;&xd7i&?1ZB-UnH;TVn`iC#P zGflwXG+2YbXQ01+3pD*U`piSo7stESpn-Fp9$jV*A?2L#XW~a5c~XV@ONoD@fPavl zKV{9=I2nh}KX{x>z=S>)eaz<(oA7MpPQ2QA500^|(y#OuVwr#b-RJSRBH&fQ#?2%9 zBB$nCd`g=bCKPY0h)majWORpI20&I}PXJj>8?TJT~; zNQmqP;&J&x~XJxm7B$NW*;`S@n?NXwG>vt?%Ba|azn4G-2Nkz&x2mFOIp z_&xiBaob`f3jrq!8PxDn78rQcCTVaU9N0JgyhVe*OL5a>*QHaxbVmCAd6%b)uDww) zBi!=0u=d|$etPsi3-NCI;`BEM?49O-=GiSCILYgua>B6N94sQZ>KIM2zG72iw~fXX zZH_#6NqYA4pO`lCNAvg@wt;Jx-vhc4MSJ!0`bY2O8VNM5(CTZgx5H{${g3&^w? z_$#Ip(;X$J{M?S3FIpeG|I0K z^Z~KvpsnCmceCV($$__m_B>$!bj_t#q~rhV(DbCo9)jPU=}W6Fx+a}@&R<&XhY~V-J@dyz5*Ir$(tDLd*|+It?~I9@ z6RhoJt=U72rM31OG2m_glOsJ1&lW~nCh&&>@xUBUpgT<&(o3K@9?A9(^yA+!((nyXf(8%OiwZs>nWnEOP=gC#4sxJ);~{NM2WzmeN@ zn{egJ+oaw{?0X%5>X5wmqw1h%KB^EMyPLOf_TFt*{3OJt^wy94PdfSAKceA%D(g4i zlg__(nS8!@)|qJrt{R?r(8JS_2Oc7ynbUX1i*NWDj$3ZR+ef|MLiD8cq=@reU54Xn zpqPl%TiKPTsIC-kObwQWpq#aFCx=q8ac2aK4}KGIpNUx=rx?(U3^W(nK}#dp%R({o?+YTVu*`GD!A0oPo=D)IBlFMmHB_>e{TJ1pbUg_kXp9D1>@F>HuV_|hbA zt4so1Xr=ROlVLdC=5n9XKiaisrBSqJ*{|HAS}PX!ad~z-!lHSy9VK6(=-NvgO&LC0 z_5D6a4qsBoIK{o>;L_F8PWgf`$A)9JtZi+pC`Vf#L~ zTDW5QP3bN^OBhW-?!G`uyQCB0h;8!E2>kLJ&PN=$IN@hPWf6$`M?1^ivLBVEd*U>l zCZIXtN&wHv7Vowo-GH}%o^#xZ>Ei2Gj3Ardq3br>oxXeSZ{#y?#!TEc+6A{OS70H> zt;7)*wuPTKfwOg5oMJ7*#|7ZCOf&_r^I!m~O1ACN0LZ>{4wZ!b70Q=w;cm?$wMBJ&i(D5(o>Fp>1a|KmCa>WFCUei8|jIFURkl_e)!1{l<(TB1=kk*hUVKS-EHe-8^u|J-X z&BL>s&G`ErzHj76%8p9k={WF^wVZH%$6!8gfv&~9`y>ACSp1yz$i7A2bJ7jqt?Sk% zenwlEyUsDO|FnIaJ7Q66l!S}UAg--~B9Ogcc%0fo;2qAIrp^bCLb@Cne-k1{a-Hu) zwG~*#)c)jH4?6L{Nm-9*WWhM_XPC%qclyKP61$Et97iv<2*~BC2g0tA(A?nT2}s`% zQ$D057u*iwFBq=ISUJ-~u8dtrop)C8pb#oF&FC_?x`%{LBNk z2=tclFeusg-kwtX1g(qQFg_aUMZ7hJKkIrgzbiwX9QX?IX8@uwxyS&N#e#5!Vv9d> zi7U-1crI}C8y>htAcwzC1ZD}e&)cM~*fNzB*V@|B=7J0zQQ&f%G||O|BT}@P2&O{n zn3aWqXM|x0H<88{M0Jpg*mOCch}HcrlB^FQs4%&!%RAF1$(X|H**Sgd*`qHW!E;Zf zTn)%Ki~Y=%W{ks+2Ih(_`R7Fex=a&>s{x#;%v!K#dfPE?O>5WU_gelxRoFVcGU7p+ z+bey*g;H8Sq84$16`xYW16=4niid*sv7Qm>a-jV_x|q7ZG_EE~|0q^DEi5*>W~Zxve{nkbtUvbd>#qUn?O{W_P0TBI*2t5bj29;O)|{SO zaD2hFj5G6Pw99pW0S5tbEY$H0p@qB7OW*$aIqB>RdjFdq;^0BvNZZhfek_rXovOSh z8EKUaB`3Q3&f$f5o3@VOKSPZJKoqssa%giy73FSV7!f&MYe&ezF8Y`l1^9@ZND?fd zF}eP~_TD^DvZ}fl-nW}>rhtlKGs_?-s0u`CeBG-2F=6dd%mbioFZzB zX3}SjpBSg-lo&rzpC%5xCkd#*ASfW9j50TKgAG00_Z!yQd!KXb-doiT`TlWtSM9ae z`mNtudpP@?s#|r+i+~%%gf?{iw+Rbn4j<*>XYgk(eD#4VVV(%}&Bk~&(&8~A$7aR6 z_>Vd@J9%gParBZ4FDf7W@W+=;<57nnT7LayFDy5$#pmRMmrnl1G9|MscGxvC^(sB> zgj(jOl=j(o@A9$Fe4+gFH@{7isf#!5{*UhD%m>Fc=VfZj@Zp9&=>+SX8N+=vzekla zeYT|7w~xv5buB4JttxelS>@`njKFw!R6onq;)Cqvou5B#8UKPmP_Hg0|H`xQ(Z}t| zW_;eg4YgS8--$d~)o{r^up=F;iB7)Urz7dT6zvrKp|IwM<@4riV)p5t^0*&7v$hftR zjw!4HMn6Zfm7_rXMDFIY$Ne5yzJK|T%7?%9dG=tY;=Xzeg0lMOj^ef7S{{7o#?feg zDS3=4(F7rpHg(*AWnP1~h6WczaVDO?D;1q5E{G%<_;<;sILr=IFocfWg7HZ+7f!X! z&}hKM_mcU=6n+D9)}mJ)_y(`PSxtW?pdcZWk-B`qR$mDqOQm4^8vH=@2S50saw|T2 zyj(d7_dx%hlU|GO1K^{iyv#{{^)=clv!;q~*uIz*y&G8dYSKd%g{f{3mOSA^Rl6TUtpMbYc@bOZ7aZp8RL@t?99#^%U!N$1I zAG~k*vsbxVo#kyI&OYb+Q)Qr^tGjb;xdgY#%?(~lq|PGv zC41>81Y?Gdc`_aPaOU|*rM>V`x8M7%*Oeb!c*#^b^dLszl}KJmtA9{A9@hr#vljaC z6&E*V`tqqCJGaAVd+pzr#$=v8*pxFuc#NRVqP2pCatfv7EsUvdATBqT9Ht$2Ea#nl zK7KjVKSGkBC)L~YBdfgiGaj1;uLyt^Uyvp@)b$=jBustOTnnr4siBKM`la&Pk9>iM zEhzZgn>Is`CNEjk*Q%PD_ilXD(|26cY}gN6vAWHhfmp|wI$ZkTBgOav*twTnjw^HU zEmyGauDkkLe39%9ecymfOMILs2)_OTch!SXeKxW5bU;3oH{w;r!F%mr{`RvUDW5rg z_K$;;gYP`}Sc_a7N)3imxYVi5!SA%AW(So>Bq3Qk&jC%^`+D$-pf?QUU{@yw>^-Va zA!Jq@_*$AQuDZE4y?TNe4nve}Y->s248p4%$#~w(Gs+fz>FM%IE-9B?c3C<2VGl3s zue}Q~&C8n67^9aQlP1JO4!nZq&EELq73=UZ(TjidvEn+^SEEu5Fw&?AI~Gikc8mA&e?t`az1t(ye$Eu}2- z2QzN%aMoqG;oTJFPOb%Rl)Oa>{rRZLg}^GfbvQ|$Ouh|b^S32l_Sc`B446inc@PV^ zg+n5P<9Qk~bPub#a7;4TLhHU5Ht%7@-qExAG=jS2q$#(ab?wNZ;_{&1PLdsh}r1i6$qa*Q(+C zfC;18q11+GSsydRrpnED*1}En`{$ffj(Y5)%C(qmy$7jy;LdnVOe=+od@zy#6!cBW zJ8*yd+iu4%NKH|$!j+0%qVj5a94}+^n|}0{)9U4mq@df3tCXGbC6|KRO8n*@zGaHY zlObMglm14)&Ts9fhL~}C;p@d2ST}5z;RIL~kB!K6o4}E+yJ@iAK%X z0Tff~)6tiYV}$Qw2_O3Mz4Pimhuat78@^MND{s1`+>E==@)zlFi%fGcIdavUO{^4q zTgBMUTYAqOuuu8e`#)38*vgL-EA}{k^rxz)=pP~ar8K;)g7h%W&Xr?E9!^2kadopz zeH~2Tr9+Kl^lIEfVw!UKb=Q_#adEa9w{*XIBYuZCoic93a4a=yOzFY_DPu0?@LLJ{ zmOuEwJMfMl-p<{M(oS}xDGtt>XBWe|7`IN}#L2eGm17Qvs%y@Ybj0{Q@;rd3A~EZf z-R1%2t%#F|2L{M@wzF@u_3e;}r4LveE@ar81MP`-?LXu~#-^Dst%zwda^oM0Cyxc9 zEnPnTp;HPDzp@(l7x7z6jO$G?b8#%hL=LITJNoe($-I*&Z%=zSZyGj5`OaD2Etg$! zb-C~TaFX%}$-EwGh(`~}WvwHP*WtW2wDX;nOZz^Y;b@mjI7lY*dHva5q zTL+8zPS(@kClPMy-MB3&JHGwsKU?9tQoix3fZq{OY>hESyhL3DNeW+jh2;g_KAdb>mif9QZ+(1Cg~=rK&q z{{7}Ix!-s>D7B4zLgtOXR&T#kS&e&tY)Sdu8F2;(;^jZFtZ7Da)UK@f&&%eI?)4!N#MK zIq{MdDxPFm>sU#fbm#?6CfgjiZ@Kp3E6OkYKYvcpmX{hQ!jVb37^BWUZ4kP+51do- zP+>m50^poMo&D0>=a>>p8=rjVP${N(`p}oZg!}GoUsmzg{@|~9=i0~z<7KSS`C<^q zd*FEzIPKhjFTeYqH%$?7afsI1DArOs=h$-2JSWw)KiRLop3v_}99pfsNmsqP?>1`( z;6a%O=6K+2^9cfj2VXsqNS-vtN#+gA#(K6{QqN>1*NvC$d?k=^@k7`SX6f5&9p+_c)s3OPh>*q3o4+8)0tF#XBsU4QXc z<@;xUuN;CO#Ma3Gi_(2>uRI*febpuj46*y{{h+ej0S_*3`;$K{ul|#_Y-zWvU%hhH zJV(43>+E+0Pc5`D>rZaU*Ux3&Pul(PdYC#KfY}c(boL|16t%|Jee>TD5QjQE>VbiI zoVn0{!2>s5#gD>FTfTArkIF|s`nggLda&-3U--9B=2G*SoFji+XB(`!mAE_Yxo4eM zj`+?d(qE_D%M2KxwzTKZmsZD+Ri)fjNdyxq?~%@Y2}1BzY4GT zr+X!`RBjoAu~S~GUVs_pSohoAYITT0KI{l{YN-n#0GmFGFYETqtPFLlIP4zC6x zmz0TZCP|o3ud3b-nazP;Zx0S}6NCq3-huEw_`b@12R*F(#~G&wXts)ZJ10-}*h=CgOmy13t{bDBR>J@F2Jfzbkv!Pw&+2qHT4;8*!tv?Qjd+U2qHAY0DkB z!PT>lIi`H+3tudUJ@SZh1Abfe#v9jS2pEvX+f8 zx60?g{1rpD@|laoSN?dbgB`7ZgJCYzO_+yeVpdN2t55Ea-N@^u$^P{^VRbjy*<+d~ zH+O|l&uRz)5Ka;C#BNH({>6c2&2-OoV`LPKQ4nH1?2FqX&r)7`%BRcr_*Bdbe&yJ* zk{?0FtrPeQXda~@6GwE8zdM0%F5_P0Q{|+;`_vG$H7$0dj){rXJ&)$>%;Ms> zbDV+?I)#DGHB1nij`PSo_{g<@yVCA5+qnMM_q-cF(DQTUWzTwUx%S4J@Hd9FxXAOk zGLsx%#X>Ik+vz@KH+)y%+MCvv*PZgN^1f3)PS{o#$H|KmcWX?_uDVd-1ySPA<`6W1 zY#tzT4h_Rrj-6c=dB&UDEcob)ogKV_dkz7tFt!;j^)^z5eV7%dz{-uGxL}vIA~A z%Nx3_#xD{*>M@VQA7^(i8+g+xtQ>ZYcP!P9Rct#~4tnSz*x0B4qRCZPUsZnoagQx; z_@g(J=O2FpPCy6akKUMA{1Kaf$YzXf;zwQ9-?6TI_w4^DpTmjceW#p)AE~+LZf57V zas1X0$J-;!-tm$8S;v)=X5Y%EdCV1%3xn4-j_P>Sa~)DUYk^PvJ)o_Yjsw(U^-=Hn zV>mrVYrk__Qq(4wxiUV`_~5`}_TF@a!7@DDLBrjTwr@ z$=#`(AZ+l=Y>#B9K;|2=UVms!gz-uY(yM#0(TMNdB#@An^s-w0WaA;kyA z@nH)N?Fwvz{G~Gf4#Y)2xfHjv{!;n)*Z#k9*|k5JHPn)L_l574cpYjyM;^V7GFR5} z^QRfQdA}l-mBoHYyLtEo)~jci(+?`QtbKae4P&zPmj6Nl(UA{~l%62kcsQ+Ue)8K5)Qap|^|V575+w@!Dp_mI&9~S@zm< zkFr1RlYhyKH%0rv*G?~dJoT_c$|LsKqwI+|lPVIt!44qQ`KRYwqfR%|d8w&WYzBxnm3&65PZ%e2lA7(a7RArPq~14>&+SM1Kc9 z$~jB<@K;VPd_4K^$CUkc-wVGfa9_OrvlCw3Z-ZYY`)Rr6#vAa7plk4Dwg15H4lI9> z#S7h8zpUpEhPW^4>$n3NTV6KtqQnF3v8c`Fp+PiPQ!Xu$iymekVDI*cV15NYlhpzn ztj`UEYg)4jL}eqIkG{Aq2*o82u--RhRP_L+JE<}7)Nz=aOhYNR+_V8d)Ui`}=JQ`t zKL4?;dsSe@$K7Nz#uSdcHNpv1$(waAbxa^ZpSn$jB#x7JZuAgX8kB3%x>b9tlOh4kq2F9#6yMqhA-CaS4`(YHb@6iC zInypaF(hBsw55!i;PAN(-@Jae)OpR+q;LQ4tJMS!e?{VMqvfuo@doBD@#D_oP5#`*o&o00E{1@PDlo(S4lX)P#@PY~I5?Di4bysBS5#u&lH_Rs+EAiyx7_opSPXZaI*1*uF2jzTlT=BLPzkv7K&wZYG{*#}# z$0rj%@%j&xKYZbePOtCFL|NnGK66!7OvsaT0}RJ)f?uzj!~vJvbRxjZ_)9%k zixFuaV4eZ#vAz89XoaYcXBCvG~-7jJ*yCFUhp-d=wDzn)a~+GoFeA^PH!FaG5hmNWk4 z8)cV0b}#3gH^bFmQ2K2gza8UG_@tbhxh81w1SVL#M%t$Av1=!QJb{km5yw{&UQ-i@ zmr!vo>f1_rP_V{BC3TD;9c{=lWK862g?{9W+A;Wy&!4ZosqFcPhm}7)_L;MBZYBDm zYj!Q?fAF2<*yldI6nvbNU#gm=Xb5zmv2Xa_$sQk$WsK*P#O*{$J{~Wm=D3Y&>N%FO z`^hH=6DUTk$cF zdj`j$duUvuT`y)j`vNS-B#*B}aPnCsUdM+ue4S1Nc={11mlNbk&dFXXfbn&m)U{z> zbD2*ViH&z0CqH&dnDLICI1O5yOnkEN;>Z&R-^#dT?dI~DH~&F-+*6Mw%{^Y211?H1 zZaD#{^(w*bTW-C*Y`^2S<)i=bg?l{qVuWve-Jg_?e&}O(pA+9~zWh3T6#^f3nz?Wp z4Ev--rdI;oyzAsn&72#0TdeKm(sW%+odDd&GGa}ff*6!`21kTHTd+; z|NGxwTOPD#H^T4r^32CPrTou7KBern>t5wNoCNsEn)+dwsiSLTHFny|&?oj!7h#k~ z4~^MK=i@NOL&r-7_xO1B($1R40*81Zf7M}6MSm{j(NQ+7n7=ZE*s(d2ygG@Lw*EnT z;Jd5-<_DvSdAQ*>z9wfU2Hfl)eEUbkv7;`^9j^-3-M#_$11aSc{88x0r#vgo_gL+` zqpuhjU)8n^Zsn>~<>HI4E04x+$Nv4N@7$=8|@6V6ERK~Z5w0UkZXKu=EjOs~1v4;8_PhZuy`JJv8oEI+VA*bT$B!IQ1 z&4f8|Vfn>O=hBu~bW*AMh&Zso;X{bl2d9WDad zh9P0B0Rjdk39_bUoZ~kU?+b0tHwUY{CW07n#SFXAG zE_^Jkl#hP>f0Y4jGLy}hp7a_gbq_sjUXZx@s)gWww;ZudaRqk%1#8QRCmvHi`j216 zZRv4);d{8e(NJ%qIo4vDxbiQ;Upt%pGK(JnWZicda$3OJJP! z;`VaaH8&`(=5FQ=MO5_*GHUlHWX&nZY70t^QH~iXoL=yyoiR9uS|7x)&3LK9x3+P? zbK#X&lmqwNr+oD<-d7IXYo9GhXla)t4|#O?^qc;^{QilrDnD6!UAgX-wfM51FSL}? z((V(itzYU(?PIJN_EUZ2+C-PbuFbs13gaOWt{WGR8OO2WAJ+f?F$GCPK~yP970j(Q zp*TbT4J+q=%%i3}`~Gvc!G307f~5_73*aD5Ajyo&iIOwpicEr3je~wYck;LvIGz*- z6n#9J=~#+mZC_7xNJ$X6n>MrEGkKTfw3oKFZb zSMe&rn+{@XVrT6q@P4nb)xo1NrHsvE!&TRoZMWZ{eDiH@F0ViCm{~hD5r6o84=SHH z>A#eJd)J%FV~;qjY`o|){3h^EFj%(2WB&E(a_NkkACl{`v&zIha4jwFUEVkDQB^MF?(`O zuF2sT!TaMAPCnsseSDNAIPpX3PgHCwvakn((h^!=5-#kl1Za5EcrX>GPOej;eG+Ff zG#jOH9B8280_Ll?+itz9++t=gA(HWB;dj*f@ zhlic#%5BVNtiRKo3Phys`7Joa_ zfy4BMLj(;trE4|wr%JUp$CRUHZ~(T!cPoB`FJ2C?K`~BCHCmsF^<;h1JRgOI5=k-nGE3dywCxAH}@W==8nA05R8^TSu&o-_% z-+nXRR$2`Xj4N+FoQ0%OxMDOOn!l83?BxJU=kaKR>bXk9&@Th!DvhIIZu8fPuO|W= z9cSdEn>5s~f&i{V7?y4)xEo-mG-xqEzKV7%3+=*}S zZp0@7w3i1Xzc|8gBkqW=pZ|0{Cou-I2G0yLV;~@|#6rFNvK#RB(01jQU;O;?>|>u_ z&i?Mdmw!L=^m5S!KPWd`e+^Dpx0JhaAa95Fc6Y>E2=~43{mQNn+^szHp${vE9CB!R z$Uz5}J@?$X+=Dv>Kn%^(78Fi@<3dT1LM!*HxcfH|NYD3k9>mKb^Szo&MtG! z&fyj}2R@{%yX|(sxp`oLfYUw1-5#-_^Y0N4eei+$)^nS)Ax{nk{dh0{QDY`|Mq zd2)$m=e!i!IWvDs{9Ol~d(hg5E8G3?eT+vQbP%q@z+6LBA(Brz=e!TTN3khmvJd3e zMi#L|6f$hQ>h$>zxAb1Oz8r#>kKS)T#nj0mqbRJNQiH3vdA!wgV426shg78IsI@Wn zh)}13tCJ1bKv%B&X*mR6iu&7w4=!&y7PmFI@Z$2VOD-u_;18fTV*T+;W2^CP@*Qyl zy8G?8Ls^3#YCi~nUwG91`<6rZ+N12S_a3+^#~NIFW7%-)t@u+1)&duMZrBWlkee|F z*bQ;gIHFvLx2L33BJl*wut(NE{6Tx_2Xb^j|KPO6D}v+ukiB*<4+Cy|lST4HZ!0%{ z-tm#R7}|i3efstV-~q3PjIzhA)(ob^%p}|-JYmrD3t?+$ZheH0##F6aLE z;&Rpxe^9=E$wj!W#0@xc-Cow;jeP*v?QzTYop6)N`{U%X?{0gShwO7u*?;$a%Y$~? zM|U>8=Em!FdkKyyzc<8Oltc9FZhYgPwkIC;q;mV6IKhpJa<{S*%VGN;QP!=$1Fr_i zm!mXxrarw|{~OBdD-pfE$te-l$LVETh`^iKXVRluKXc{$_B(I80hc)4$$^L{TI|_q zZdKC+Qnn<9Cl{h>NV{~ss+LY?5WL1Zw2g-#*#Ipui80((g9e)dW?@_WM(j?wkiyM7 z3-0x(O!?Y?cXZ%=rS8IoEM;={F>$M(at97#zLMbHM9-+$fQ&$cxDM>APi!p49N;xs8 zH-mILI6q()+}oZnK%D||L&srvzO2cSHD3|jT5i4V*0Ks$d(8$4VCHc!d=**)BnsNw1W%{qIBpEGmPSv{5)9; zkRwhxKxbapsY`0v0+RviX2Gn2QxQWZE;q)?!%2+=Pfl)HTHt6@tT3R&71jn!CN~^n z`B2g9M|%+0jm#nIOpY()xw$hQ<;~b|=drQyWg}zst1A0Ookx-Rksob^iNNqxA9#g` z;+WwhU%G@}9VaYE#ASSY!JctYGoMx1kXGY8S?T3Zo*0MVOCRRLTT1ZO85^;wB68+)wz`K9Qs2;e7B8k(=;I0nJa?S0oNKeH9C?ygbzpxroE< zRfwtEM-v-<8sFLmu8td1!+U>8xQ}f zL=f#GRIr_aQ8&Gn^jgWci~=VXnULL}ro}o+8#U(YDb(%>^%_Cs!`of(Z5iueXuU<= ziFZxg1{^!UA?oo;b9@vkR4d|UdsPOv z)JlTcqay!_IxPAj>fjhI3Qt8QWjNxQ1 zC|rpF9ODHKW%VR{5j-(6br?*wbbNOF?io05mhh8v%TN-?c+imm+psn-1}9WX=yO?# z2MbCErjme7oI=VYnhWHnaJJmu`10K^!7~Pvq3r0E+^5WenYo`~Wm4`K^!URla@UE3 zL@49U)_v=3_SbEC)o--L88r?j-uimuM!a1{PO4Qi6SncFaiCM1HI>fC`4Z1U0{gYu zGMC%5^9MorgI(BUGbg#(FXOgYsdr5JB$w|q347$q^ytTjGK#rpZX49kh)G~zSt}3f z=*6lQY&PNouYl1@RFZlu71=gS3Z}4;ZSGWfi%f;>#AuOOEq6YpjX6Q)CfFC@pwjxH zoyOqN-;@HLdX5H5(&+71wv$L!QmoL%e)lGRp~^kBCu5+EhHBx{jx~$e?Ib~7+QQz) zNPX}qp=u|G`4zkBldsv>9b>rx^8$-UcjO=&2Ox3~r#qNAvejaxOs#XylcnMa%-AS; za-Hq0>6;~Cc4&IrX|>L{L{Grwge7s*%_G)%=o`TK0-R}s6L zsW(?Sp{XDlsq0*lX3c@`3tMy>Y8#?bHkBmd!#OcMhdDh#bl`gDB(~BT-bQJGt^(n} z|4<1@@^Iu;5F>F}hh0Z5bejCGuD*%$X6yOp z+hKea#!<(J!A<&6T`9Ct!HB}_oL47FWNeQd&ehA!#Hqyw zO6mqR8u+i_2)*`=E(OlH?4YoXdH2O_={hWlsWWyc+Z#F9h~qsiR+(sl&ABQd}66KNZq2%df5++AO~e&eU%`m&@=;IIY)|8pRQ zHnxVr)1I7Jr+!k+J*jnOi0DksAZ1#z&LLwvijRIYQkONeO24j-1DH$1gPOR7=$dfl z&X}m-);vSMph6VHW5`yJOXOgNFs8a@MFqTMt^yQsA`;&E0X?0>Z*BIHFUno0&j&Rr z=aOyh2tL3}(zO_!uNG6b87KQd9m>G&LsJ>lMaY)*ikbMRHGiV_`1;O~5pOfzKEa7V z{@lRfV?43Z29#9&+&CWdWFc2z>*6I#vr(!i2IjdeMp>bAgT{_EXakUprQ&-?u_&y!VG<9r{1I2`KuF6PX~@=#Wk{~=eUzAd=*4K z;U^=rV_suE`iB1~md|$S#{f(|{mE@z()-7bQcoibxjp-C`&quBNIC07xUB($YG^ek zZ9$~Ued#socU6$5_v>YXTAW;ec%Agm$G`n}d<)fOa3Y{;Wm$Rhrn2c}Hs|xCL||h) z#b|+B$n=f6Vw=8tO|Xr5DmFu+o~-jEwGI;qHvD%Od!?`AaM`pz_6)S!&pg@~C_L9HCSajp*WAsC$3zft$cXvz#EnGHGj)KJ;@~*0emnqpUjXJ z+XE&3WOOXNKQBCeSkk|*$9l9i)?h%LZ`}~p{ChdMpyOL23gtdC2jXRcJChF40dg$Y08!RhUzNJTvS!Khkf^R@L z+UZP$2$$*Rr1!)V`pzAbMb`ZseomF{rgfI(xH0iT^!^WrK~w^s1S-LEg$N8&S-b>c{DBLU|!_!ZM{rdwux~r zneR|1;{cnybfnL)ZM^ze`JwA@4G&*n%tMUx8~bLPSyE0P;o0UN0F?_lf;0aPz_)*d z;Yr8X75FtV{^oD%KiVW8bEl4^dbE6L^2vY_3o+0|Z9=K=#H2!YAU7a$kEdPU0}RxC zJ7n!uy`J7(+dNhb^aIS$&ffR&vz=V5B?oSxM-bbCGclb$?HFEQ6Ys=n^B;PlPoJ!b zh0N)-jqQ-BCk3&|n|=e`7_*Q|%SW-?QRc)!wVrq?P!z|tnxRj7)-hOw$T{8J)-w(I zpRKX2awR|{88DSuPv8o|oOU$7f8yJZIW#?3FL*^j<)#%Y|8T|T&EKKimk8adI_ge4 zNnGgLP>Zs_&<}zRl)j&^d&4pp=1T+Wiq9@s9(6u=UB~}7Xxsz9HG<7+c zJH2y0V>1CK`%UKLP!v3|U(!3ph;Dyj=##!v)db*#gel)y+&F8882NH;-GRbVwMjX;+AG|)SNoW+Wf;6Ubz zd@`4?lS*_|PoGFI`l+^np{G4^$UIwo#OcPz{PuI4jHd)5_VPL-GCq?|Hu7|Onkwr; z8#O%_S<*Y#PqqcETseNg&&|Kiw)Q;qY5MY|>o@;1h@a7?+nFH_T?hRd0$XcwGWV?^ ztCX8@RRl+k-f>FTx2w!)M(KSc;f4AJY_Z`l-{gt9LQ_vgj7$y){OS}8O*#npFC&2_eRAKM;HW5%i!KUb8vusfMt4==>;e?bK zYXo3pJ4QnnGF4tm590J2)Df5*)N8aUs2x6N)81Ycxb0T}kZA{xec==Md?m2Z&v=GO zjN%8+z`tU8-i+xy`AnZ-O@#+bCUOf9Pijy`30WI{W*^eRIMhyBA^wY`h6unD0y*3hTxEmdTig~HYsa=l7I1YV?qce!7?ZloMBZ&#q-tHEcO2XXFSyCu65J^XTo#ofwDAerImOC^`1lR!`;H z@<=_e{97M*@cYPxohJztpG2sJ4IhQ{1vzc-1WaQCL2+xFevT#%JfW##JjKj3^a2>= zaiks+9T}JGuo7~Ec=>%}W$3`Pn2e3P1 zu*fC-)aK|1rvL#A2ga&)`LXSBuQqig$D*&_zE8sDYrh|yvPKXS_aI3Af3 zsg!Qm-JlOG9-!nBQn}KY{U?q^4yaQ3E>j{}ht-nx{L(l^_zr_m9^lJ!zD zyH`1V)VeJ>^J}$_?5?f+-S${@HgI^FI9N?ioe#p1jm;6K*YO1ZisQ6)JYDYGm_5c( zE2oi880yGlo9m(M80nk&ICkyRfP?V&Lk*qwXb zoD<7DqK~Yp&e#LF#vxC`c+9ii`6rja%%5@CASO8a8a84C>Gkr*Q~1_+iLpcYGLB

*x#nRP$)q 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 { CustomButton( text: LocaleKeys.loginOrRegister.tr(context: context), onPressed: () { - Navigator.of(context).pushReplacement( - MaterialPageRoute(builder: (BuildContext context) => LandingPage()), + Navigator.of(context).push( + MaterialPageRoute(builder: (BuildContext context) => LoginScreen()), ); }, backgroundColor: Color(0xffFEE9EA), @@ -64,7 +68,14 @@ class _LandingPageState extends State { icon: AppAssets.contact_icon, width: 24, height: 24, - ), + ).onPress(() { + Navigator.of(context).push( + FadePage( + page: MedicalFilePage(), + // page: LoginScreen(), + ), + ); + }), ], ), ), @@ -114,7 +125,7 @@ class _LandingPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - "Quick Links".toText14(isBold: true), + "Quick Links".toText16(isBold: true), Row( children: [ "View medical file".toText12(color: AppColors.primaryRedColor), @@ -217,7 +228,7 @@ class _LandingPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - "Services".toText14(isBold: true), + "Services".toText16(isBold: true), Row( children: [ "View all services".toText12(color: AppColors.primaryRedColor), @@ -262,25 +273,7 @@ class _LandingPageState extends State { ), ), SizedBox(height: 16.h), - AppState().isAuthenticated - ? Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "My Balance".toText14(isBold: true), - Row( - children: [ - "View all services".toText12(color: AppColors.primaryRedColor), - SizedBox(width: 2.h), - Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), - ], - ), - ], - ), - ], - ) - : SizedBox(), + AppState().isAuthenticated ? HabibWalletCard() : SizedBox(), ], ), ), diff --git a/lib/presentation/home/widgets/habib_wallet_card.dart b/lib/presentation/home/widgets/habib_wallet_card.dart new file mode 100644 index 0000000..9b513db --- /dev/null +++ b/lib/presentation/home/widgets/habib_wallet_card.dart @@ -0,0 +1,127 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; + +class HabibWalletCard extends StatelessWidget { + const HabibWalletCard({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "My Balance".toText16(isBold: true), + Row( + children: [ + "View all services".toText12(color: AppColors.primaryRedColor), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), + ], + ), + ], + ), + SizedBox(height: 16.h), + Container( + // height: 150.h, + width: double.infinity, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24, + ), + child: Stack(children: [ + Positioned( + right: 0, + child: ClipRRect(borderRadius: BorderRadius.circular(24.0), child: Utils.buildSvgWithAssets(icon: AppAssets.habib_background_icon, width: 150.h, height: 150.h)), + ), + Padding( + padding: EdgeInsets.all(14.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Habib Wallet".toText15(isBold: true), + Container( + height: 40.h, + width: 40.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.textColor, + borderRadius: 8.h, + ), + child: Padding( + padding: EdgeInsets.all(8.h), + child: Utils.buildSvgWithAssets( + icon: AppAssets.show_icon, + width: 12.h, + height: 12.h, + fit: BoxFit.contain, + ), + ), + ), + ], + ), + SizedBox(height: 4.h), + Row( + children: [ + Utils.buildSvgWithAssets( + icon: AppAssets.saudi_riyal_icon, + iconColor: AppColors.dividerColor, + width: 24.h, + height: 24.h, + fit: BoxFit.contain, + ), + SizedBox(width: 8.h), + "200.18".toText32(isBold: true), + ], + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 50.h), + child: Row( + children: [ + "View details".toText12(color: AppColors.primaryRedColor), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), + ], + ), + ), + SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox( + width: 150.h, + child: Utils.getPaymentMethods(), + ), + CustomButton( + icon: AppAssets.recharge_icon, + iconSize: 18.h, + text: "Recharge", + onPressed: () {}, + backgroundColor: AppColors.infoColor, + borderColor: AppColors.infoColor, + textColor: AppColors.whiteColor, + fontSize: 12, + fontWeight: FontWeight.bold, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 35.h, + ), + ], + ), + ], + ), + ), + ]), + ) + ], + ); + } +} diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index dff7f75..55ca84b 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -1,5 +1,13 @@ +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/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/input_widget.dart'; class MedicalFilePage extends StatelessWidget { const MedicalFilePage({super.key}); @@ -12,12 +20,152 @@ class MedicalFilePage extends StatelessWidget { title: const Text('Appointments'), backgroundColor: AppColors.bgScaffoldColor, ), - body: const Center( - child: Text( - 'Appointments Page', - style: TextStyle(fontSize: 24), + body: Padding( + padding: EdgeInsets.all(24.h), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.medicalFile.tr(context: context).toText22(isBold: true), + SizedBox(height: 16.h), + TextInputWidget( + labelText: LocaleKeys.search.tr(context: context), + hintText: "Type any record", + controller: TextEditingController(), + keyboardType: TextInputType.number, + isEnable: true, + prefix: null, + autoFocus: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h), + leadingIcon: AppAssets.student_card, + hasError: true, + ), + SizedBox(height: 16.h), + Container( + width: double.infinity, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset( + AppAssets.male_img, + width: 56.h, + height: 56.h, + ), + SizedBox(width: 8.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Haroon Amjad".toText18(isBold: true), + SizedBox(height: 4.h), + Row( + children: [ + CustomButton( + icon: AppAssets.file_icon, + iconColor: AppColors.blackColor, + iconSize: 12.h, + text: "File no: 3628599", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 10, + fontWeight: FontWeight.normal, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + SizedBox(width: 4.h), + CustomButton( + icon: AppAssets.checkmark_icon, + iconColor: AppColors.successColor, + iconSize: 13.h, + text: LocaleKeys.verified.tr(context: context), + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 10, + fontWeight: FontWeight.normal, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + ], + ) + ], + ), + SizedBox(height: 16.h), + Divider(color: AppColors.dividerColor, height: 1.h), + SizedBox(height: 16.h), + Row( + children: [ + CustomButton( + text: "30 Years Old", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 10, + fontWeight: FontWeight.normal, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + SizedBox(width: 4.h), + CustomButton( + icon: AppAssets.blood_icon, + iconColor: AppColors.primaryRedColor, + iconSize: 13.h, + text: "Blood: A+", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 10, + fontWeight: FontWeight.normal, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + SizedBox(width: 4.h), + CustomButton( + icon: AppAssets.insurance_active_icon, + iconColor: AppColors.successColor, + iconSize: 13.h, + text: "Insurance Active", + onPressed: () {}, + backgroundColor: AppColors.bgGreenColor.withOpacity(0.20), + borderColor: AppColors.bgGreenColor.withOpacity(0.0), + textColor: AppColors.blackColor, + fontSize: 10, + fontWeight: FontWeight.normal, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + SizedBox(height: 8.h), + ], + ), + ), + ) + ], + ), ), ), ); } -} \ No newline at end of file +} diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index 3eacbd6..ff310ee 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -29,6 +29,7 @@ class AppColors { static const Color bgGreenColor = Color(0xFF18C273); static const Color textColor = Color(0xFF2E3039); static const Color borderOnlyColor = Color(0xFF2E3039); + static const Color dividerColor = Color(0xFFD2D2D2); //Chips static const Color successColor = Color(0xff18C273); diff --git a/lib/widgets/buttons/custom_button.dart b/lib/widgets/buttons/custom_button.dart index e08189a..5413cea 100644 --- a/lib/widgets/buttons/custom_button.dart +++ b/lib/widgets/buttons/custom_button.dart @@ -20,6 +20,7 @@ class CustomButton extends StatelessWidget { final bool isDisabled; final Color iconColor; final double height; + final double iconSize; CustomButton({ Key? key, @@ -38,6 +39,7 @@ class CustomButton extends StatelessWidget { this.icon, this.iconColor = Colors.white, this.height = 56, + this.iconSize = 24, }) : super(key: key); @override @@ -62,7 +64,7 @@ class CustomButton extends StatelessWidget { if (icon != null) Padding( padding: const EdgeInsets.only(right: 8.0), - child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled), + child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconSize, height: iconSize), ), Text( text, From 9509d4a1921b9d07f4bed68c77862e9e155ab072 Mon Sep 17 00:00:00 2001 From: faizatflutter Date: Tue, 2 Sep 2025 15:15:16 +0300 Subject: [PATCH 3/4] completed the deviceapi with new architecture --- lib/core/api/api_client.dart | 10 +- lib/core/app_state.dart | 2 +- lib/core/dependencies.dart | 6 +- lib/core/enums.dart | 42 +- lib/core/exceptions/api_failure.dart | 9 + lib/core/location_util.dart | 6 +- ...CalendarUtils.dart => calendar_utils.dart} | 0 ...fication.dart => local_notifications.dart} | 0 ...er.dart => push_notification_handler.dart} | 12 +- lib/core/utils/request_utils.dart | 91 +++ lib/extensions/widget_extensions.dart | 22 +- .../authentication/authentication_repo.dart | 111 +++- .../authentication_view_model.dart | 103 +++- ...heck_activation_code_request_register.dart | 121 ---- ...heck_activation_code_request_register.dart | 121 ++++ ..._patient_authentication_request_model.dart | 88 +++ .../send_activation_request_model.dart | 132 +++++ .../authenticated_user_resp_model.dart} | 0 .../check_activation_code_resp_model.dart | 546 ++++++++++++++++++ ...ent_last_login_details_response_model.dart | 0 .../select_device_by_imei.dart | 0 lib/services/analytics/analytics_service.dart | 2 +- lib/services/dialog_service.dart | 18 +- lib/services/error_handler_service.dart | 11 +- lib/splashPage.dart | 4 +- lib/widgets/chip/custom_chip_widget.dart | 6 +- 26 files changed, 1286 insertions(+), 177 deletions(-) rename lib/core/utils/{CalendarUtils.dart => calendar_utils.dart} (100%) rename lib/core/utils/{LocalNotification.dart => local_notifications.dart} (100%) rename lib/core/utils/{push-notification-handler.dart => push_notification_handler.dart} (97%) create mode 100644 lib/core/utils/request_utils.dart delete mode 100644 lib/features/authentication/models/check_activation_code_request_register.dart create mode 100644 lib/features/authentication/models/request_models/check_activation_code_request_register.dart create mode 100644 lib/features/authentication/models/request_models/check_patient_authentication_request_model.dart create mode 100644 lib/features/authentication/models/request_models/send_activation_request_model.dart rename lib/features/authentication/models/{authenticated_user_model.dart => resp_models/authenticated_user_resp_model.dart} (100%) create mode 100644 lib/features/authentication/models/resp_models/check_activation_code_resp_model.dart rename lib/features/authentication/models/{ => resp_models}/get_patient_last_login_details_response_model.dart (100%) rename lib/features/authentication/models/{ => resp_models}/select_device_by_imei.dart (100%) diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index c3862e2..92a7e6c 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -170,6 +170,14 @@ class ApiClientImp implements ApiClient { } } + // request.versionID = VERSION_ID; + // request.channel = CHANNEL; + // request.iPAdress = IP_ADDRESS; + // request.generalid = GENERAL_ID; + // request.languageID = (languageID == 'ar' ? 1 : 2); + // request.patientOutSA = (request.zipCode == '966' || request.zipCode == '+966') ? 0 : 1; + + // TODO : we will use all these from appState body['LanguageID'] = body['LanguageID'] ?? "2"; body['VersionID'] = body['VersionID'] ?? "18.7"; body['Channel'] = body['Channel'] ?? "3"; @@ -192,7 +200,7 @@ class ApiClientImp implements ApiClient { final int statusCode = response.statusCode; if (statusCode < 200 || statusCode >= 400) { - onFailure('Error While Fetching data', statusCode, failureType: ServerFailure("Error While Fetching data")); + onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data")); logApiEndpointError(endPoint, 'Error While Fetching data', statusCode); } else { var parsed = json.decode(utf8.decode(response.bodyBytes)); diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart index bdf8d25..7c42042 100644 --- a/lib/core/app_state.dart +++ b/lib/core/app_state.dart @@ -1,6 +1,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:hmg_patient_app_new/core/post_params_model.dart'; -import 'package:hmg_patient_app_new/features/authentication/models/authenticated_user_model.dart'; +import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'api_consts.dart' as ApiConsts; diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index 5aba3e2..549eeed 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -35,7 +35,11 @@ class AppDependencies { getIt.registerLazySingleton(() => NavigationService()); getIt.registerLazySingleton(() => GAnalytics()); getIt.registerLazySingleton(() => AppState(navigationService: getIt())); - getIt.registerLazySingleton(() => LocationUtils(isShowConfirmDialog: false, navigationService: getIt())); + getIt.registerLazySingleton(() => LocationUtils( + isShowConfirmDialog: false, + navigationService: getIt(), + appState: getIt(), + )); getIt.registerLazySingleton(() => DialogServiceImp(navigationService: getIt())); getIt.registerLazySingleton(() => ErrorHandlerServiceImp( dialogService: getIt(), diff --git a/lib/core/enums.dart b/lib/core/enums.dart index db758f3..2fd5867 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -5,7 +5,7 @@ // unverified, // } -enum AuthMethodTypes { +enum AuthMethodTypesEnum { sms, whatsApp, fingerPrint, @@ -13,7 +13,7 @@ enum AuthMethodTypes { moreOptions, } -enum ViewState { +enum ViewStateEnum { hide, idle, busy, @@ -22,20 +22,44 @@ enum ViewState { errorLocal, } -enum LoginType { +enum LoginTypeEnum { fromLogin, silentLogin, silentWithOTP, } -enum OTPType { sms, whatsapp } +enum OTPTypeEnum { sms, whatsapp } -enum Country { saudiArabia, unitedArabEmirates } +enum CountryEnum { saudiArabia, unitedArabEmirates } -enum SelectionType { dropdown, calendar } +enum SelectionTypeEnum { dropdown, calendar } -enum GenderType { male, female } +enum GenderTypeEnum { male, female } -enum MaritalStatusType { single, married, divorced, widowed } +enum MaritalStatusTypeEnum { single, married, divorced, widowed } -enum ChipType { success, error, alert, info, warning } +enum ChipTypeEnum { success, error, alert, info, warning } + +extension OTPTypeEnumExtension on OTPTypeEnum { + /// Convert enum to int + int toInt() { + switch (this) { + case OTPTypeEnum.sms: + return 1; + case OTPTypeEnum.whatsapp: + return 2; + } + } + + /// Convert int to enum + static OTPTypeEnum fromInt(int value) { + switch (value) { + case 1: + return OTPTypeEnum.sms; + case 2: + return OTPTypeEnum.whatsapp; + default: + throw Exception("Invalid OTPTypeEnum value: $value"); + } + } +} diff --git a/lib/core/exceptions/api_failure.dart b/lib/core/exceptions/api_failure.dart index d2a510f..4bc5097 100644 --- a/lib/core/exceptions/api_failure.dart +++ b/lib/core/exceptions/api_failure.dart @@ -13,6 +13,13 @@ class ServerFailure extends Failure { List get props => [message]; } +class StatusCodeFailure extends Failure { + const StatusCodeFailure(super.message); + + @override + List get props => [message]; +} + class ConnectivityFailure extends Failure { const ConnectivityFailure(super.message); @@ -55,3 +62,5 @@ class InvalidCredentials extends Failure { @override List get props => [message]; } + + diff --git a/lib/core/location_util.dart b/lib/core/location_util.dart index c8e69af..dc6a0d6 100644 --- a/lib/core/location_util.dart +++ b/lib/core/location_util.dart @@ -4,14 +4,14 @@ import 'package:geolocator/geolocator.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/consts.dart'; -import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; -// import 'package:huawei_location/huawei_location.dart'; import 'package:permission_handler/permission_handler.dart'; class LocationUtils { NavigationService navigationService; + AppState appState; + bool isShowConfirmDialog; bool isShowLocationTimeoutDialog; bool isHuawei; @@ -20,10 +20,10 @@ class LocationUtils { LocationUtils({ required this.isShowConfirmDialog, required this.navigationService, + required this.appState, this.isHuawei = false, this.isShowLocationTimeoutDialog = true, }); - AppState appState = getIt.get(); void getCurrentLocation({Function(LatLng)? callBack}) async { Geolocator.isLocationServiceEnabled().then((value) async { diff --git a/lib/core/utils/CalendarUtils.dart b/lib/core/utils/calendar_utils.dart similarity index 100% rename from lib/core/utils/CalendarUtils.dart rename to lib/core/utils/calendar_utils.dart diff --git a/lib/core/utils/LocalNotification.dart b/lib/core/utils/local_notifications.dart similarity index 100% rename from lib/core/utils/LocalNotification.dart rename to lib/core/utils/local_notifications.dart diff --git a/lib/core/utils/push-notification-handler.dart b/lib/core/utils/push_notification_handler.dart similarity index 97% rename from lib/core/utils/push-notification-handler.dart rename to lib/core/utils/push_notification_handler.dart index d71bdcf..b5a816e 100644 --- a/lib/core/utils/push-notification-handler.dart +++ b/lib/core/utils/push_notification_handler.dart @@ -3,19 +3,17 @@ import 'dart:developer'; import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:firebase_messaging/firebase_messaging.dart' as fir; import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart' as fir; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; // import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -import 'package:hmg_patient_app_new/core/utils/LocalNotification.dart'; +import 'package:hmg_patient_app_new/core/utils/local_notifications.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:permission_handler/permission_handler.dart'; -import 'package:uuid/uuid.dart'; import '../consts.dart'; @@ -365,7 +363,9 @@ class PushNotificationHandler { Future requestPermissions() async { try { if (Platform.isIOS) { - await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation()?.requestPermissions(alert: true, badge: true, sound: true); + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation() + ?.requestPermissions(alert: true, badge: true, sound: true); } else if (Platform.isAndroid) { Map statuses = await [ Permission.notification, diff --git a/lib/core/utils/request_utils.dart b/lib/core/utils/request_utils.dart new file mode 100644 index 0000000..1869923 --- /dev/null +++ b/lib/core/utils/request_utils.dart @@ -0,0 +1,91 @@ +import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/features/authentication/models/request_models/send_activation_request_model.dart'; + +class RequestUtils { + static dynamic getCommonRequestWelcome({ + required String phoneNumber, + required OTPTypeEnum otpTypeEnum, + required String? deviceToken, + required bool patientOutSA, + required String? loginTokenID, + required var registeredData, + required int? patientId, + required String nationIdText, + required String countryCode, + }) { + bool fileNo = false; + if (nationIdText.isNotEmpty) { + fileNo = nationIdText.length < 10; + } + var request = SendActivationRequest(); + request.patientMobileNumber = int.parse(phoneNumber); + request.mobileNo = '0$phoneNumber'; + request.deviceToken = deviceToken; + request.projectOutSA = patientOutSA; + request.loginType = otpTypeEnum.toInt(); + request.oTPSendType = otpTypeEnum.toInt(); // could map OTPTypeEnum if needed + request.zipCode = countryCode; // or countryCode if defined elsewhere + request.logInTokenID = loginTokenID ?? ""; + + if (registeredData != null) { + request.searchType = registeredData.searchType ?? 1; + request.patientID = registeredData.patientID ?? 0; + request.patientIdentificationID = request.nationalID = registeredData.patientIdentificationID ?? '0'; + request.dob = registeredData.dob; + request.isRegister = registeredData.isRegister; + } else { + if (fileNo) { + request.patientID = patientId ?? int.parse(nationIdText); + request.patientIdentificationID = request.nationalID = ""; + request.searchType = 2; + } else { + request.patientID = 0; + request.searchType = 1; + request.patientIdentificationID = request.nationalID = nationIdText.isNotEmpty ? nationIdText : '0'; + } + request.isRegister = false; + } + + request.deviceTypeID = request.searchType; + return request; + } + + static getCommonRequestAuthProvider({ + required OTPTypeEnum otpTypeEnum, + required registeredData, + required deviceToken, + required mobileNumber, + required zipCode, + required patientOutSA, + required loginTokenID, + required selectedOption, + required int? patientId, + }) { + var request = SendActivationRequest(); + request.patientMobileNumber = mobileNumber; + request.mobileNo = '0$mobileNumber'; + request.deviceToken = deviceToken; + request.projectOutSA = patientOutSA == true ? true : false; + request.loginType = selectedOption; + request.oTPSendType = otpTypeEnum.toInt(); //this.selectedOption == 1 ? 1 : 2; + request.zipCode = zipCode; + + request.logInTokenID = loginTokenID ?? ""; + + if (registeredData != null) { + request.searchType = registeredData.searchType ?? 1; + request.patientID = registeredData.patientID ?? 0; + request.patientIdentificationID = request.nationalID = registeredData.patientIdentificationID ?? '0'; + request.dob = registeredData.dob; + request.isRegister = registeredData.isRegister; + } else { + request.searchType = request.searchType ?? 2; + request.patientID = patientId ?? 0; + request.nationalID = request.nationalID ?? '0'; + request.patientIdentificationID = request.patientIdentificationID ?? '0'; + request.isRegister = false; + } + request.deviceTypeID = request.searchType; + return request; + } +} diff --git a/lib/extensions/widget_extensions.dart b/lib/extensions/widget_extensions.dart index 8b8c796..e6913d2 100644 --- a/lib/extensions/widget_extensions.dart +++ b/lib/extensions/widget_extensions.dart @@ -193,33 +193,33 @@ Widget widthSpacer5per() => SizedBox(height: 5.w); -extension ChipTypeExtension on ChipType { +extension ChipTypeEnumExtension on ChipTypeEnum { Color get color { switch (this) { - case ChipType.success: + case ChipTypeEnum.success: return AppColors.successColor; // Replace with your actual color - case ChipType.error: + case ChipTypeEnum.error: return AppColors.errorColor; // Replace with your actual color - case ChipType.alert: + case ChipTypeEnum.alert: return AppColors.alertColor; // Replace with your actual color - case ChipType.info: + case ChipTypeEnum.info: return AppColors.infoColor; // Replace with your actual color - case ChipType.warning: + case ChipTypeEnum.warning: return AppColors.warningColor; // Replace with your actual color } } Color get backgroundColor { switch (this) { - case ChipType.success: + case ChipTypeEnum.success: return AppColors.successLightColor; // Replace with your actual color - case ChipType.error: + case ChipTypeEnum.error: return AppColors.errorLightColor; // Replace with your actual color - case ChipType.alert: + case ChipTypeEnum.alert: return AppColors.alertLightColor; // Replace with your actual color - case ChipType.info: + case ChipTypeEnum.info: return AppColors.infoLightColor; // Replace with your actual color - case ChipType.warning: + case ChipTypeEnum.warning: return AppColors.warningLightColor; // Replace with your actual color } } diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart index 04e3a9e..7307013 100644 --- a/lib/features/authentication/authentication_repo.dart +++ b/lib/features/authentication/authentication_repo.dart @@ -1,15 +1,27 @@ import 'dart:async'; +import 'dart:io'; import 'package:dartz/dartz.dart'; 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/authentication/models/select_device_by_imei.dart'; +import 'package:hmg_patient_app_new/features/authentication/models/request_models/check_patient_authentication_request_model.dart'; +import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_activation_code_resp_model.dart'; +import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; abstract class AuthenticationRepo { - Future>> selectDeviceByImei({required String firebaseToken}); + Future>> selectDeviceByImei({ + required String firebaseToken, + }); + + Future>> checkPatientAuthentication({ + required CheckPatientAuthenticationReq checkPatientAuthenticationReq, + }); + + Future>> sendActivationCodeRegister( + {required CheckPatientAuthenticationReq checkPatientAuthenticationReq, String? languageID}); } class AuthenticationRepoImp implements AuthenticationRepo { @@ -19,9 +31,7 @@ class AuthenticationRepoImp implements AuthenticationRepo { AuthenticationRepoImp({required this.loggerService, required this.apiClient}); @override - Future>> selectDeviceByImei({ - required String firebaseToken, - }) async { + Future>> selectDeviceByImei({required String firebaseToken}) async { final mapDevice = {"IMEI": firebaseToken}; try { GenericApiModel? apiResponse; @@ -34,7 +44,7 @@ class AuthenticationRepoImp implements AuthenticationRepo { }, onSuccess: (response, statusCode, {messageStatus}) { try { - final list = response['Patient_SELECTDeviceIMEIbyIMEIList'] as List?; + final list = response['Patient_SELECTDeviceIMEIbyIMEIList']; if (list == null || list.isEmpty) { throw Exception("Device list is empty"); } @@ -58,4 +68,93 @@ class AuthenticationRepoImp implements AuthenticationRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>> checkPatientAuthentication({ + required CheckPatientAuthenticationReq checkPatientAuthenticationReq, + String? languageID, + }) async { + int isOutKsa = (checkPatientAuthenticationReq.zipCode == '966' || checkPatientAuthenticationReq.zipCode == '+966') ? 0 : 1; + //TODO : We will use all these from AppState directly in the ApiClient + + checkPatientAuthenticationReq.versionID = VERSION_ID; + checkPatientAuthenticationReq.channel = CHANNEL; + checkPatientAuthenticationReq.iPAdress = IP_ADDRESS; + checkPatientAuthenticationReq.generalid = GENERAL_ID; + checkPatientAuthenticationReq.languageID = (languageID == 'ar' ? 1 : 2); + checkPatientAuthenticationReq.patientOutSA = isOutKsa; + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + ApiConsts.selectDeviceImei, + body: checkPatientAuthenticationReq.toJson(), + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future>> sendActivationCodeRegister( + {required CheckPatientAuthenticationReq checkPatientAuthenticationReq, String? languageID}) async { + int isOutKsa = (checkPatientAuthenticationReq.zipCode == '966' || checkPatientAuthenticationReq.zipCode == '+966') ? 0 : 1; + //TODO : We will use all these from AppState directly in the ApiClient + + checkPatientAuthenticationReq.versionID = VERSION_ID; + checkPatientAuthenticationReq.channel = CHANNEL; + checkPatientAuthenticationReq.iPAdress = IP_ADDRESS; + checkPatientAuthenticationReq.generalid = GENERAL_ID; + checkPatientAuthenticationReq.languageID = (languageID == 'ar' ? 1 : 2); + checkPatientAuthenticationReq.deviceTypeID = Platform.isIOS ? 1 : 2; + checkPatientAuthenticationReq.patientOutSA = isOutKsa; + checkPatientAuthenticationReq.isDentalAllowedBackend = false; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + SEND_ACTIVATION_CODE_REGISTER, + body: checkPatientAuthenticationReq.toJson(), + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: CheckActivationCode.fromJson(response), + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index eaf3c95..573c528 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/request_utils.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart'; +import 'package:hmg_patient_app_new/features/authentication/models/request_models/check_patient_authentication_request_model.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; @@ -24,15 +27,109 @@ class AuthenticationViewModel extends ChangeNotifier { String firebaseToken = "dOGRRszQQMGe_9wA5Hx3kO:APA91bFV5IcIJXvcCXXk0tc2ddtZgWwCPq7sGSuPr-YW7iiJpQZKgFGN9GAzCVOWL8MfheaP1slE8MdxB7lczdPBGdONQ7WbMmhgHcsUCUktq-hsapGXXqc"; final result = await authenticationRepo.selectDeviceByImei(firebaseToken: firebaseToken); + result.fold( - (failure) async => await errorHandlerService.handleError(failure), + (failure) async => await errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.messageStatus == 2) { - dialogService.showErrorDialog(apiResponse.errorMessage!); + dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); } else if (apiResponse.messageStatus == 1) { - // move to next api call + //todo: move to next api call + } + }, + ); + } + + Future checkUserAuthentication({Function(dynamic)? onSuccess, Function(String)? onError}) async { + CheckPatientAuthenticationReq checkPatientAuthenticationReq = RequestUtils.getCommonRequestWelcome( + phoneNumber: '0567184134', + otpTypeEnum: OTPTypeEnum.sms, + deviceToken: 'dummyDeviceToken123', + patientOutSA: true, + loginTokenID: 'dummyLoginToken456', + registeredData: null, + patientId: 12345, + nationIdText: '1234567890', + countryCode: 'SA', + ); + + final result = await authenticationRepo.checkPatientAuthentication(checkPatientAuthenticationReq: checkPatientAuthenticationReq); + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.data['isSMSSent']) { + // TODO: set this in AppState + // sharedPref.setString(LOGIN_TOKEN_ID, value['LogInTokenID']); + // loginTokenID = value['LogInTokenID'], + // sharedPref.setObject(REGISTER_DATA_FOR_LOGIIN, request), + sendActivationCode(type); + } else { + if (apiResponse.data['IsAuthenticated']) { + checkActivationCode(onWrongActivationCode: (String? message) {}); + } } }, ); } + + Future sendActivationCode({required OTPTypeEnum otpTypeEnum}) async { + var request = RequestUtils.getCommonRequestAuthProvider( + otpTypeEnum: otpTypeEnum, + registeredData: null, + deviceToken: "dummyLoginToken456", + mobileNumber: "0567184134", + zipCode: "SA", + patientOutSA: true, + loginTokenID: "dummyLoginToken456", + selectedOption: selectedOption, + patientId: 12345, + ); + + request.sMSSignature = await SMSOTP.getSignature(); + selectedOption = type; + // GifLoaderDialogUtils.showMyDialog(context); + if (healthId != null || isDubai) { + if (!isDubai) { + request.dob = dob; //isHijri == 1 ? dob : dateFormat2.format(dateFormat.parse(dob)); + } + request.healthId = healthId; + request.isHijri = isHijri; + await this.apiClient.sendActivationCodeRegister(request).then((result) { + // GifLoaderDialogUtils.hideDialog(context); + if (result != null && result['isSMSSent'] == true) { + this.startSMSService(type); + } + }).catchError((r) { + GifLoaderDialogUtils.hideDialog(context); + context.showBottomSheet( + child: ExceptionBottomSheet( + message: r.toString(), + onOkPressed: () { + Navigator.of(context).pop(); + }, + )); + // AppToast.showErrorToast(message: r); + }); + } else { + request.dob = ""; + request.healthId = ""; + request.isHijri = 0; + await this.authService.sendActivationCode(request).then((result) { + GifLoaderDialogUtils.hideDialog(context); + if (result != null && result['isSMSSent'] == true) { + this.startSMSService(type); + } + }).catchError((r) { + GifLoaderDialogUtils.hideDialog(context); + context.showBottomSheet( + child: ExceptionBottomSheet( + message: r.toString(), + onOkPressed: () { + Navigator.of(context).pop(); + }, + )); + // AppToast.showErrorToast(message: r.toString()); + }); + } + } } diff --git a/lib/features/authentication/models/check_activation_code_request_register.dart b/lib/features/authentication/models/check_activation_code_request_register.dart deleted file mode 100644 index 85e1f56..0000000 --- a/lib/features/authentication/models/check_activation_code_request_register.dart +++ /dev/null @@ -1,121 +0,0 @@ -class CheckActivationCodeRegisterReq { - int? patientMobileNumber; - String? mobileNo; - String? deviceToken; - bool? projectOutSA; - int? loginType; - String? zipCode; - bool? isRegister; - String? logInTokenID; - int? searchType; - int? patientID; - String? nationalID; - String? patientIdentificationID; - String? activationCode; - bool? isSilentLogin; - double? versionID; - int? channel; - int? languageID; - String? iPAdress; - String? generalid; - int? patientOutSA; - dynamic sessionID; - bool? isDentalAllowedBackend; - int? deviceTypeID; - bool? forRegisteration; - String? dob; - int? isHijri; - String? healthId; - - CheckActivationCodeRegisterReq({ - this.patientMobileNumber, - this.mobileNo, - this.deviceToken, - this.projectOutSA, - this.loginType, - this.zipCode, - this.isRegister, - this.logInTokenID, - this.searchType, - this.patientID, - this.nationalID, - this.patientIdentificationID, - this.activationCode, - this.isSilentLogin, - this.versionID, - this.channel, - this.languageID, - this.iPAdress, - this.generalid, - this.patientOutSA, - this.sessionID, - this.isDentalAllowedBackend, - this.deviceTypeID, - this.forRegisteration, - this.dob, - this.isHijri, - this.healthId, - }); - - CheckActivationCodeRegisterReq.fromJson(Map json) { - patientMobileNumber = json['PatientMobileNumber']; - mobileNo = json['MobileNo']; - deviceToken = json['DeviceToken']; - projectOutSA = json['ProjectOutSA']; - loginType = json['LoginType']; - zipCode = json['ZipCode']; - isRegister = json['isRegister']; - logInTokenID = json['LogInTokenID']; - searchType = json['SearchType']; - patientID = json['PatientID']; - nationalID = json['NationalID']; - patientIdentificationID = json['PatientIdentificationID']; - activationCode = json['activationCode']; - isSilentLogin = json['IsSilentLogin']; - 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']; - forRegisteration = json['ForRegisteration']; - dob = json['DOB']; - isHijri = json['IsHijri']; - healthId = json['HealthId']; - } - - Map toJson() { - final Map data = new Map(); - data['PatientMobileNumber'] = this.patientMobileNumber; - data['MobileNo'] = this.mobileNo; - data['DeviceToken'] = this.deviceToken; - data['ProjectOutSA'] = this.projectOutSA; - data['LoginType'] = this.loginType; - data['ZipCode'] = this.zipCode; - data['isRegister'] = this.isRegister; - data['LogInTokenID'] = this.logInTokenID; - data['SearchType'] = this.searchType; - data['PatientID'] = this.patientID; - data['NationalID'] = this.nationalID; - data['PatientIdentificationID'] = this.patientIdentificationID; - data['activationCode'] = this.activationCode; - data['IsSilentLogin'] = this.isSilentLogin; - 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['ForRegisteration'] = this.forRegisteration; - data['DOB'] = dob; - data['IsHijri'] = isHijri; - data['HealthId'] = healthId; - return data; - } -} diff --git a/lib/features/authentication/models/request_models/check_activation_code_request_register.dart b/lib/features/authentication/models/request_models/check_activation_code_request_register.dart new file mode 100644 index 0000000..167c8ec --- /dev/null +++ b/lib/features/authentication/models/request_models/check_activation_code_request_register.dart @@ -0,0 +1,121 @@ +// class CheckActivationCodeRegisterReq { +// int? patientMobileNumber; +// String? mobileNo; +// String? deviceToken; +// bool? projectOutSA; +// int? loginType; +// String? zipCode; +// bool? isRegister; +// String? logInTokenID; +// int? searchType; +// int? patientID; +// String? nationalID; +// String? patientIdentificationID; +// String? activationCode; +// bool? isSilentLogin; +// double? versionID; +// int? channel; +// int? languageID; +// String? iPAdress; +// String? generalid; +// int? patientOutSA; +// dynamic sessionID; +// bool? isDentalAllowedBackend; +// int? deviceTypeID; +// bool? forRegisteration; +// String? dob; +// int? isHijri; +// String? healthId; +// +// CheckActivationCodeRegisterReq({ +// this.patientMobileNumber, +// this.mobileNo, +// this.deviceToken, +// this.projectOutSA, +// this.loginType, +// this.zipCode, +// this.isRegister, +// this.logInTokenID, +// this.searchType, +// this.patientID, +// this.nationalID, +// this.patientIdentificationID, +// this.activationCode, +// this.isSilentLogin, +// this.versionID, +// this.channel, +// this.languageID, +// this.iPAdress, +// this.generalid, +// this.patientOutSA, +// this.sessionID, +// this.isDentalAllowedBackend, +// this.deviceTypeID, +// this.forRegisteration, +// this.dob, +// this.isHijri, +// this.healthId, +// }); +// +// CheckActivationCodeRegisterReq.fromJson(Map json) { +// patientMobileNumber = json['PatientMobileNumber']; +// mobileNo = json['MobileNo']; +// deviceToken = json['DeviceToken']; +// projectOutSA = json['ProjectOutSA']; +// loginType = json['LoginType']; +// zipCode = json['ZipCode']; +// isRegister = json['isRegister']; +// logInTokenID = json['LogInTokenID']; +// searchType = json['SearchType']; +// patientID = json['PatientID']; +// nationalID = json['NationalID']; +// patientIdentificationID = json['PatientIdentificationID']; +// activationCode = json['activationCode']; +// isSilentLogin = json['IsSilentLogin']; +// 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']; +// forRegisteration = json['ForRegisteration']; +// dob = json['DOB']; +// isHijri = json['IsHijri']; +// healthId = json['HealthId']; +// } +// +// Map toJson() { +// final Map data = new Map(); +// data['PatientMobileNumber'] = this.patientMobileNumber; +// data['MobileNo'] = this.mobileNo; +// data['DeviceToken'] = this.deviceToken; +// data['ProjectOutSA'] = this.projectOutSA; +// data['LoginType'] = this.loginType; +// data['ZipCode'] = this.zipCode; +// data['isRegister'] = this.isRegister; +// data['LogInTokenID'] = this.logInTokenID; +// data['SearchType'] = this.searchType; +// data['PatientID'] = this.patientID; +// data['NationalID'] = this.nationalID; +// data['PatientIdentificationID'] = this.patientIdentificationID; +// data['activationCode'] = this.activationCode; +// data['IsSilentLogin'] = this.isSilentLogin; +// 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['ForRegisteration'] = this.forRegisteration; +// data['DOB'] = dob; +// data['IsHijri'] = isHijri; +// data['HealthId'] = healthId; +// return data; +// } +// } diff --git a/lib/features/authentication/models/request_models/check_patient_authentication_request_model.dart b/lib/features/authentication/models/request_models/check_patient_authentication_request_model.dart new file mode 100644 index 0000000..483e416 --- /dev/null +++ b/lib/features/authentication/models/request_models/check_patient_authentication_request_model.dart @@ -0,0 +1,88 @@ +class CheckPatientAuthenticationReq { + int? patientMobileNumber; + String? zipCode; + bool? isRegister; + String? tokenID; + int? searchType; + String? patientIdentificationID; + int? patientID; + double? versionID; + int? channel; + int? languageID; + String? iPAdress; + String? generalid; + int? patientOutSA; + dynamic sessionID; + bool? isDentalAllowedBackend; + int? deviceTypeID; + String? dob; + int? isHijri; + String? healthId; + + CheckPatientAuthenticationReq( + {this.patientMobileNumber, + this.zipCode, + this.isRegister, + this.tokenID, + this.searchType, + this.patientIdentificationID, + this.patientID, + this.versionID, + this.channel, + this.languageID, + this.iPAdress, + this.generalid, + this.patientOutSA, + this.sessionID, + this.isDentalAllowedBackend, + this.deviceTypeID, + this.dob, + this.isHijri, + this.healthId}); + + CheckPatientAuthenticationReq.fromJson(Map json) { + patientMobileNumber = json['PatientMobileNumber']; + zipCode = json['ZipCode']; + isRegister = json['isRegister']; + tokenID = json['TokenID']; + searchType = json['SearchType']; + patientIdentificationID = json['PatientIdentificationID']; + patientID = json['PatientID']; + 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']; + dob = json['dob']; + isHijri = json['isHijri']; + healthId = json['HealthId']; + } + + Map toJson() { + final Map data = {}; + data['PatientMobileNumber'] = patientMobileNumber; + data['ZipCode'] = zipCode; + data['isRegister'] = isRegister; + data['TokenID'] = tokenID; + data['SearchType'] = searchType; + data['PatientIdentificationID'] = patientIdentificationID; + data['PatientID'] = patientID; + data['VersionID'] = versionID; + data['Channel'] = channel; + data['LanguageID'] = languageID; + data['IPAdress'] = iPAdress; + data['generalid'] = generalid; + data['PatientOutSA'] = patientOutSA; + data['SessionID'] = sessionID; + data['isDentalAllowedBackend'] = isDentalAllowedBackend; + data['DeviceTypeID'] = deviceTypeID; + data['dob'] = dob; + data['isHijri'] = isHijri; + data['HealthId'] = healthId; + return data; + } +} diff --git a/lib/features/authentication/models/request_models/send_activation_request_model.dart b/lib/features/authentication/models/request_models/send_activation_request_model.dart new file mode 100644 index 0000000..dc3d55d --- /dev/null +++ b/lib/features/authentication/models/request_models/send_activation_request_model.dart @@ -0,0 +1,132 @@ +class SendActivationRequest { + int? patientMobileNumber; + String? mobileNo; + String? deviceToken; + bool? projectOutSA; + int? loginType; + String? zipCode; + bool? isRegister; + String? logInTokenID; + int? searchType; + int? patientID; + String? nationalID; + String? patientIdentificationID; + int? oTPSendType; + int? languageID; + double? versionID; + int? channel; + String? iPAdress; + String? generalid; + int? patientOutSA; + dynamic sessionID; + bool? isDentalAllowedBackend; + int? deviceTypeID; + String? sMSSignature; + String? dob; + int? isHijri; + String? healthId; + int? responseID; + int? status; + int? familyRegionID; + bool? isPatientExcluded; + SendActivationRequest( + {this.patientMobileNumber, + this.mobileNo, + this.deviceToken, + this.projectOutSA, + this.loginType, + this.zipCode, + this.isRegister, + this.logInTokenID, + this.searchType, + this.patientID, + this.nationalID, + this.patientIdentificationID, + this.oTPSendType, + this.languageID, + this.versionID, + this.channel, + this.iPAdress, + this.generalid, + this.patientOutSA, + this.sessionID, + this.isDentalAllowedBackend, + this.deviceTypeID, + this.sMSSignature, + this.dob, + this.isHijri, + this.healthId, + this.responseID, + this.status, + this.familyRegionID, + this.isPatientExcluded + }); + + SendActivationRequest.fromJson(Map json) { + patientMobileNumber = json['PatientMobileNumber']; + mobileNo = json['MobileNo']; + deviceToken = json['DeviceToken']; + projectOutSA = json['ProjectOutSA']; + loginType = json['LoginType']; + zipCode = json['ZipCode']; + isRegister = json['isRegister']; + logInTokenID = json['LogInTokenID']; + searchType = json['SearchType']; + patientID = json['PatientID']; + nationalID = json['NationalID']; + patientIdentificationID = json['PatientIdentificationID']; + oTPSendType = json['OTP_SendType']; + languageID = json['LanguageID']; + versionID = json['VersionID']; + channel = json['Channel']; + iPAdress = json['IPAdress']; + generalid = json['generalid']; + patientOutSA = json['PatientOutSA']; + sessionID = json['SessionID']; + isDentalAllowedBackend = json['isDentalAllowedBackend']; + deviceTypeID = json['DeviceTypeID']; + sMSSignature = json['SMSSignature']; + dob = json['DOB']; + isHijri = json['IsHijri']; + healthId = json['HealthId']; + responseID = json['ReponseID']; + status = json['Status']; + familyRegionID = json['FamilyRegionID']; + isPatientExcluded = json['IsPatientExcluded']; + } + + Map toJson() { + final Map data = new Map(); + data['PatientMobileNumber'] = patientMobileNumber; + data['MobileNo'] = mobileNo; + data['DeviceToken'] = deviceToken; + data['ProjectOutSA'] = projectOutSA; + data['LoginType'] = loginType; + data['ZipCode'] = zipCode; + data['isRegister'] = isRegister; + data['LogInTokenID'] = logInTokenID; + data['SearchType'] = searchType; + data['PatientID'] = patientID; + data['NationalID'] = nationalID; + data['PatientIdentificationID'] = patientIdentificationID; + data['OTP_SendType'] = oTPSendType; + data['LanguageID'] = languageID; + data['VersionID'] = versionID; + data['Channel'] = channel; + data['IPAdress'] = iPAdress; + data['generalid'] = generalid; + data['PatientOutSA'] = patientOutSA; + data['SessionID'] = sessionID; + data['isDentalAllowedBackend'] = isDentalAllowedBackend; + data['DeviceTypeID'] = deviceTypeID; + data['SMSSignature'] = sMSSignature; + data['DOB'] = dob; + data['IsHijri'] = isHijri; + data['HealthId'] = healthId; + data['ResponseID'] = responseID; + data['Status'] = status; + data['FamilyRegionID'] = familyRegionID; + data['IsPatientExcluded'] = isPatientExcluded; + return data; + } +} diff --git a/lib/features/authentication/models/authenticated_user_model.dart b/lib/features/authentication/models/resp_models/authenticated_user_resp_model.dart similarity index 100% rename from lib/features/authentication/models/authenticated_user_model.dart rename to lib/features/authentication/models/resp_models/authenticated_user_resp_model.dart diff --git a/lib/features/authentication/models/resp_models/check_activation_code_resp_model.dart b/lib/features/authentication/models/resp_models/check_activation_code_resp_model.dart new file mode 100644 index 0000000..8610e08 --- /dev/null +++ b/lib/features/authentication/models/resp_models/check_activation_code_resp_model.dart @@ -0,0 +1,546 @@ +import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart'; + +class CheckActivationCode { + dynamic date; + int? languageID; + int? serviceName; + dynamic time; + dynamic androidLink; + String? authenticationTokenID; + dynamic data; + bool? dataw; + int? dietType; + dynamic errorCode; + dynamic errorEndUserMessage; + dynamic errorEndUserMessageN; + dynamic errorMessage; + int? errorType; + int? foodCategory; + dynamic iOSLink; + bool? isAuthenticated; + int? mealOrderStatus; + int? mealType; + int? messageStatus; + int? numberOfResultRecords; + dynamic patientBlodType; + dynamic successMsg; + dynamic successMsgN; + dynamic doctorInformationList; + dynamic getAllPendingRecordsList; + dynamic getAllSharedRecordsByStatusList; + dynamic getResponseFileList; + bool? isHMGPatient; + bool? isLoginSuccessfully; + bool? isNeedUpdateIdintificationNo; + bool? kioskSendSMS; + AuthenticatedUser? list; + dynamic listAskHabibMobileLoginInfo; + dynamic listAskHabibPatientFile; + dynamic listMergeFiles; + dynamic listMobileLoginInfo; + dynamic listPatientCount; + dynamic logInTokenID; + dynamic mohemmPrivilegeList; + int? pateintID; + String? patientBloodType; + bool? patientHasFile; + dynamic patientMergedIDs; + bool? patientOutSA; + int? patientShareRequestID; + int? patientType; + int? projectIDOut; + dynamic returnMessage; + bool? sMSLoginRequired; + dynamic servicePrivilegeList; + dynamic sharePatientName; + dynamic verificationCode; + dynamic email; + dynamic errorList; + bool? hasFile; + bool? isActiveCode; + bool? isMerged; + bool? isNeedUserAgreement; + bool? isSMSSent; + dynamic memberList; + dynamic message; + int? statusCode; + + CheckActivationCode( + {this.date, + this.languageID, + this.serviceName, + this.time, + this.androidLink, + this.authenticationTokenID, + this.data, + this.dataw, + this.dietType, + this.errorCode, + this.errorEndUserMessage, + this.errorEndUserMessageN, + this.errorMessage, + this.errorType, + this.foodCategory, + this.iOSLink, + this.isAuthenticated, + this.mealOrderStatus, + this.mealType, + this.messageStatus, + this.numberOfResultRecords, + this.patientBlodType, + this.successMsg, + this.successMsgN, + this.doctorInformationList, + this.getAllPendingRecordsList, + this.getAllSharedRecordsByStatusList, + this.getResponseFileList, + this.isHMGPatient, + this.isLoginSuccessfully, + this.isNeedUpdateIdintificationNo, + this.kioskSendSMS, + this.list, + this.listAskHabibMobileLoginInfo, + this.listAskHabibPatientFile, + this.listMergeFiles, + this.listMobileLoginInfo, + this.listPatientCount, + this.logInTokenID, + this.mohemmPrivilegeList, + this.pateintID, + this.patientBloodType, + this.patientHasFile, + this.patientMergedIDs, + this.patientOutSA, + this.patientShareRequestID, + this.patientType, + this.projectIDOut, + this.returnMessage, + this.sMSLoginRequired, + this.servicePrivilegeList, + this.sharePatientName, + this.verificationCode, + this.email, + this.errorList, + this.hasFile, + this.isActiveCode, + this.isMerged, + this.isNeedUserAgreement, + this.isSMSSent, + this.memberList, + this.message, + this.statusCode}); + + CheckActivationCode.fromJson(Map json) { + date = json['Date']; + languageID = json['LanguageID']; + serviceName = json['ServiceName']; + time = json['Time']; + androidLink = json['AndroidLink']; + authenticationTokenID = json['AuthenticationTokenID']; + data = json['Data']; + dataw = json['Dataw']; + dietType = json['DietType']; + errorCode = json['ErrorCode']; + errorEndUserMessage = json['ErrorEndUserMessage']; + errorEndUserMessageN = json['ErrorEndUserMessageN']; + errorMessage = json['ErrorMessage']; + errorType = json['ErrorType']; + foodCategory = json['FoodCategory']; + iOSLink = json['IOSLink']; + isAuthenticated = json['IsAuthenticated']; + mealOrderStatus = json['MealOrderStatus']; + mealType = json['MealType']; + messageStatus = json['MessageStatus']; + numberOfResultRecords = json['NumberOfResultRecords']; + patientBlodType = json['PatientBlodType']; + successMsg = json['SuccessMsg']; + successMsgN = json['SuccessMsgN']; + doctorInformationList = json['DoctorInformation_List']; + getAllPendingRecordsList = json['GetAllPendingRecordsList']; + getAllSharedRecordsByStatusList = json['GetAllSharedRecordsByStatusList']; + getResponseFileList = json['GetResponseFileList']; + isHMGPatient = json['IsHMGPatient']; + isLoginSuccessfully = json['IsLoginSuccessfully']; + isNeedUpdateIdintificationNo = json['IsNeedUpdateIdintificationNo']; + kioskSendSMS = json['KioskSendSMS']; + if (json['List'] != null) { + list = AuthenticatedUser.fromJson(json['List'][0]); + } + listAskHabibMobileLoginInfo = json['List_AskHabibMobileLoginInfo']; + listAskHabibPatientFile = json['List_AskHabibPatientFile']; + listMergeFiles = json['List_MergeFiles']; + listMobileLoginInfo = json['List_MobileLoginInfo']; + listPatientCount = json['List_PatientCount']; + logInTokenID = json['LogInTokenID']; + mohemmPrivilegeList = json['MohemmPrivilege_List']; + pateintID = json['PateintID']; + patientBloodType = json['PatientBloodType']; + patientHasFile = json['PatientHasFile']; + patientMergedIDs = json['PatientMergedIDs']; + patientOutSA = json['PatientOutSA']; + patientShareRequestID = json['PatientShareRequestID']; + patientType = json['PatientType']; + projectIDOut = json['ProjectIDOut']; + returnMessage = json['ReturnMessage']; + sMSLoginRequired = json['SMSLoginRequired']; + servicePrivilegeList = json['ServicePrivilege_List']; + sharePatientName = json['SharePatientName']; + verificationCode = json['VerificationCode']; + email = json['email']; + errorList = json['errorList']; + hasFile = json['hasFile']; + isActiveCode = json['isActiveCode']; + isMerged = json['isMerged']; + isNeedUserAgreement = json['isNeedUserAgreement']; + isSMSSent = json['isSMSSent']; + memberList = json['memberList']; + message = json['message']; + statusCode = json['statusCode']; + } + + Map toJson() { + final Map data = {}; + data['Date'] = date; + data['LanguageID'] = languageID; + data['ServiceName'] = serviceName; + data['Time'] = time; + data['AndroidLink'] = androidLink; + data['AuthenticationTokenID'] = authenticationTokenID; + data['Data'] = this.data; + data['Dataw'] = dataw; + data['DietType'] = dietType; + data['ErrorCode'] = errorCode; + data['ErrorEndUserMessage'] = errorEndUserMessage; + data['ErrorEndUserMessageN'] = errorEndUserMessageN; + data['ErrorMessage'] = errorMessage; + data['ErrorType'] = errorType; + data['FoodCategory'] = foodCategory; + data['IOSLink'] = iOSLink; + data['IsAuthenticated'] = isAuthenticated; + data['MealOrderStatus'] = mealOrderStatus; + data['MealType'] = mealType; + data['MessageStatus'] = messageStatus; + data['NumberOfResultRecords'] = numberOfResultRecords; + data['PatientBlodType'] = patientBlodType; + data['SuccessMsg'] = successMsg; + data['SuccessMsgN'] = successMsgN; + data['DoctorInformation_List'] = doctorInformationList; + data['GetAllPendingRecordsList'] = getAllPendingRecordsList; + data['GetAllSharedRecordsByStatusList'] = getAllSharedRecordsByStatusList; + data['GetResponseFileList'] = getResponseFileList; + data['IsHMGPatient'] = isHMGPatient; + data['IsLoginSuccessfully'] = isLoginSuccessfully; + data['IsNeedUpdateIdintificationNo'] = isNeedUpdateIdintificationNo; + data['KioskSendSMS'] = kioskSendSMS; + if (list != null) { + data['List'] = list; + } + data['List_AskHabibMobileLoginInfo'] = listAskHabibMobileLoginInfo; + data['List_AskHabibPatientFile'] = listAskHabibPatientFile; + data['List_MergeFiles'] = listMergeFiles; + data['List_MobileLoginInfo'] = listMobileLoginInfo; + data['List_PatientCount'] = listPatientCount; + data['LogInTokenID'] = logInTokenID; + data['MohemmPrivilege_List'] = mohemmPrivilegeList; + data['PateintID'] = pateintID; + data['PatientBloodType'] = patientBloodType; + data['PatientHasFile'] = patientHasFile; + data['PatientMergedIDs'] = patientMergedIDs; + data['PatientOutSA'] = patientOutSA; + data['PatientShareRequestID'] = patientShareRequestID; + data['PatientType'] = patientType; + data['ProjectIDOut'] = projectIDOut; + data['ReturnMessage'] = returnMessage; + data['SMSLoginRequired'] = sMSLoginRequired; + data['ServicePrivilege_List'] = servicePrivilegeList; + data['SharePatientName'] = sharePatientName; + data['VerificationCode'] = verificationCode; + data['email'] = email; + data['errorList'] = errorList; + data['hasFile'] = hasFile; + data['isActiveCode'] = isActiveCode; + data['isMerged'] = isMerged; + data['isNeedUserAgreement'] = isNeedUserAgreement; + data['isSMSSent'] = isSMSSent; + data['memberList'] = memberList; + data['message'] = message; + data['statusCode'] = statusCode; + return data; + } +} + +class List { + String? setupID; + int? patientType; + int? patientID; + String? firstName; + String? middleName; + String? lastName; + String? firstNameN; + String? middleNameN; + String? lastNameN; + int? relationshipID; + int? gender; + String? dateofBirth; + dynamic dateofBirthN; + String? nationalityID; + dynamic phoneResi; + dynamic phoneOffice; + String? mobileNumber; + dynamic faxNumber; + String? emailAddress; + dynamic bloodGroup; + dynamic rHFactor; + bool? isEmailAlertRequired; + bool? isSMSAlertRequired; + String? preferredLanguage; + bool? isPrivilegedMember; + dynamic memberID; + dynamic expiryDate; + dynamic isHmgEmployee; + dynamic employeeID; + dynamic emergencyContactName; + dynamic emergencyContactNo; + int? patientPayType; + dynamic dHCCPatientRefID; + bool? isPatientDummy; + int? status; + dynamic isStatusCleared; + int? patientIdentificationType; + String? patientIdentificationNo; + int? projectID; + int? infoSourceID; + dynamic address; + int? age; + String? ageDesc; + int? areaID; + int? createdBy; + String? genderDescription; + dynamic iR; + dynamic iSOCityID; + dynamic iSOCountryID; + ListPrivilege? listPrivilege; + dynamic marital; + int? outSA; + dynamic pOBox; + bool? receiveHealthSummaryReport; + int? sourceType; + dynamic strDateofBirth; + dynamic tempAddress; + dynamic zipCode; + + List({ + this.setupID, + this.patientType, + this.patientID, + this.firstName, + this.middleName, + this.lastName, + this.firstNameN, + this.middleNameN, + this.lastNameN, + this.relationshipID, + this.gender, + this.dateofBirth, + this.dateofBirthN, + this.nationalityID, + this.phoneResi, + this.phoneOffice, + this.mobileNumber, + this.faxNumber, + this.emailAddress, + this.bloodGroup, + this.rHFactor, + this.isEmailAlertRequired, + this.isSMSAlertRequired, + this.preferredLanguage, + this.isPrivilegedMember, + this.memberID, + this.expiryDate, + this.isHmgEmployee, + this.employeeID, + this.emergencyContactName, + this.emergencyContactNo, + this.patientPayType, + this.dHCCPatientRefID, + this.isPatientDummy, + this.status, + this.isStatusCleared, + this.patientIdentificationType, + this.patientIdentificationNo, + this.projectID, + this.infoSourceID, + this.address, + this.age, + this.ageDesc, + this.areaID, + this.createdBy, + this.genderDescription, + this.iR, + this.iSOCityID, + this.iSOCountryID, + this.listPrivilege, + this.marital, + this.outSA, + this.pOBox, + this.receiveHealthSummaryReport, + this.sourceType, + this.strDateofBirth, + this.tempAddress, + this.zipCode, + }); + + List.fromJson(Map json) { + setupID = json['SetupID']; + patientType = json['PatientType']; + patientID = json['PatientID']; + firstName = json['FirstName']; + middleName = json['MiddleName']; + lastName = json['LastName']; + firstNameN = json['FirstNameN']; + middleNameN = json['MiddleNameN']; + lastNameN = json['LastNameN']; + relationshipID = json['RelationshipID']; + gender = json['Gender']; + dateofBirth = json['DateofBirth']; + dateofBirthN = json['DateofBirthN']; + nationalityID = json['NationalityID']; + phoneResi = json['PhoneResi']; + phoneOffice = json['PhoneOffice']; + mobileNumber = json['MobileNumber']; + faxNumber = json['FaxNumber']; + emailAddress = json['EmailAddress']; + bloodGroup = json['BloodGroup']; + rHFactor = json['RHFactor']; + isEmailAlertRequired = json['IsEmailAlertRequired']; + isSMSAlertRequired = json['IsSMSAlertRequired']; + preferredLanguage = json['PreferredLanguage']; + isPrivilegedMember = json['IsPrivilegedMember']; + memberID = json['MemberID']; + expiryDate = json['ExpiryDate']; + isHmgEmployee = json['IsHmgEmployee']; + employeeID = json['EmployeeID']; + emergencyContactName = json['EmergencyContactName']; + emergencyContactNo = json['EmergencyContactNo']; + patientPayType = json['PatientPayType']; + dHCCPatientRefID = json['DHCCPatientRefID']; + isPatientDummy = json['IsPatientDummy']; + status = json['Status']; + isStatusCleared = json['IsStatusCleared']; + patientIdentificationType = json['PatientIdentificationType']; + patientIdentificationNo = json['PatientIdentificationNo']; + projectID = json['ProjectID']; + infoSourceID = json['InfoSourceID']; + address = json['Address']; + age = json['Age']; + ageDesc = json['AgeDesc']; + areaID = json['AreaID']; + createdBy = json['CreatedBy']; + genderDescription = json['GenderDescription']; + iR = json['IR']; + iSOCityID = json['ISOCityID']; + iSOCountryID = json['ISOCountryID']; + if (json['ListPrivilege'] != null) { + listPrivilege = ListPrivilege.fromJson(json['ListPrivilege']); + } + marital = json['Marital']; + outSA = json['OutSA']; + pOBox = json['POBox']; + receiveHealthSummaryReport = json['ReceiveHealthSummaryReport']; + sourceType = json['SourceType']; + strDateofBirth = json['StrDateofBirth']; + tempAddress = json['TempAddress']; + zipCode = json['ZipCode']; + } + + Map toJson() { + final Map data = {}; + data['SetupID'] = setupID; + data['PatientType'] = patientType; + data['PatientID'] = patientID; + data['FirstName'] = firstName; + data['MiddleName'] = middleName; + data['LastName'] = lastName; + data['FirstNameN'] = firstNameN; + data['MiddleNameN'] = middleNameN; + data['LastNameN'] = lastNameN; + data['RelationshipID'] = relationshipID; + data['Gender'] = gender; + data['DateofBirth'] = dateofBirth; + data['DateofBirthN'] = dateofBirthN; + data['NationalityID'] = nationalityID; + data['PhoneResi'] = phoneResi; + data['PhoneOffice'] = phoneOffice; + data['MobileNumber'] = mobileNumber; + data['FaxNumber'] = faxNumber; + data['EmailAddress'] = emailAddress; + data['BloodGroup'] = bloodGroup; + data['RHFactor'] = rHFactor; + data['IsEmailAlertRequired'] = isEmailAlertRequired; + data['IsSMSAlertRequired'] = isSMSAlertRequired; + data['PreferredLanguage'] = preferredLanguage; + data['IsPrivilegedMember'] = isPrivilegedMember; + data['MemberID'] = memberID; + data['ExpiryDate'] = expiryDate; + data['IsHmgEmployee'] = isHmgEmployee; + data['EmployeeID'] = employeeID; + data['EmergencyContactName'] = emergencyContactName; + data['EmergencyContactNo'] = emergencyContactNo; + data['PatientPayType'] = patientPayType; + data['DHCCPatientRefID'] = dHCCPatientRefID; + data['IsPatientDummy'] = isPatientDummy; + data['Status'] = status; + data['IsStatusCleared'] = isStatusCleared; + data['PatientIdentificationType'] = patientIdentificationType; + data['PatientIdentificationNo'] = patientIdentificationNo; + data['ProjectID'] = projectID; + data['InfoSourceID'] = infoSourceID; + data['Address'] = address; + data['Age'] = age; + data['AgeDesc'] = ageDesc; + data['AreaID'] = areaID; + data['CreatedBy'] = createdBy; + data['GenderDescription'] = genderDescription; + data['IR'] = iR; + data['ISOCityID'] = iSOCityID; + data['ISOCountryID'] = iSOCountryID; + if (listPrivilege != null) { + data['ListPrivilege'] = listPrivilege; + } + data['Marital'] = marital; + data['OutSA'] = outSA; + data['POBox'] = pOBox; + data['ReceiveHealthSummaryReport'] = receiveHealthSummaryReport; + data['SourceType'] = sourceType; + data['StrDateofBirth'] = strDateofBirth; + data['TempAddress'] = tempAddress; + data['ZipCode'] = zipCode; + + return data; + } +} + +class ListPrivilege { + int? iD; + String? serviceName; + bool? previlege; + dynamic region; + + ListPrivilege({this.iD, this.serviceName, this.previlege, this.region}); + + ListPrivilege.fromJson(Map json) { + iD = json['ID']; + serviceName = json['ServiceName']; + previlege = json['Previlege']; + region = json['Region']; + } + + Map toJson() { + final Map data = {}; + data['ID'] = iD; + data['ServiceName'] = serviceName; + data['Previlege'] = previlege; + data['Region'] = region; + return data; + } +} diff --git a/lib/features/authentication/models/get_patient_last_login_details_response_model.dart b/lib/features/authentication/models/resp_models/get_patient_last_login_details_response_model.dart similarity index 100% rename from lib/features/authentication/models/get_patient_last_login_details_response_model.dart rename to lib/features/authentication/models/resp_models/get_patient_last_login_details_response_model.dart diff --git a/lib/features/authentication/models/select_device_by_imei.dart b/lib/features/authentication/models/resp_models/select_device_by_imei.dart similarity index 100% rename from lib/features/authentication/models/select_device_by_imei.dart rename to lib/features/authentication/models/resp_models/select_device_by_imei.dart diff --git a/lib/services/analytics/analytics_service.dart b/lib/services/analytics/analytics_service.dart index d4f83a4..3411157 100644 --- a/lib/services/analytics/analytics_service.dart +++ b/lib/services/analytics/analytics_service.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:geolocator/geolocator.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; -import 'package:hmg_patient_app_new/features/authentication/models/authenticated_user_model.dart'; +import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart'; import 'package:hmg_patient_app_new/services/analytics/flows/advance_payments.dart'; import 'package:hmg_patient_app_new/services/analytics/flows/app_nav.dart'; import 'package:hmg_patient_app_new/services/analytics/flows/appointments.dart'; diff --git a/lib/services/dialog_service.dart b/lib/services/dialog_service.dart index e931568..af5af9c 100644 --- a/lib/services/dialog_service.dart +++ b/lib/services/dialog_service.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/extensions/route_extensions.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; abstract class DialogService { - Future showErrorDialog(String message); + Future showErrorDialog({required String message, Function()? onOkPressed}); } class DialogServiceImp implements DialogService { @@ -11,7 +12,7 @@ class DialogServiceImp implements DialogService { DialogServiceImp({required this.navigationService}); @override - Future showErrorDialog(String message) async { + Future showErrorDialog({required String message, Function()? onOkPressed}) async { final context = navigationService.navigatorKey.currentContext; if (context == null) return; @@ -21,15 +22,16 @@ class DialogServiceImp implements DialogService { shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), - builder: (_) => _ErrorBottomSheet(message: message), + builder: (_) => _ErrorBottomSheet(message: message, onOkPressed: onOkPressed), ); } } class _ErrorBottomSheet extends StatelessWidget { final String message; + final Function()? onOkPressed; - const _ErrorBottomSheet({required this.message}); + const _ErrorBottomSheet({required this.message, this.onOkPressed}); @override Widget build(BuildContext context) { @@ -55,7 +57,13 @@ class _ErrorBottomSheet extends StatelessWidget { ), const SizedBox(height: 16), ElevatedButton( - onPressed: () => Navigator.of(context).pop(), + onPressed: () { + if (onOkPressed != null) { + onOkPressed!(); + } else { + context.pop(); + } + }, style: ElevatedButton.styleFrom( backgroundColor: Colors.red, shape: RoundedRectangleBorder( diff --git a/lib/services/error_handler_service.dart b/lib/services/error_handler_service.dart index d8d473a..a26d264 100644 --- a/lib/services/error_handler_service.dart +++ b/lib/services/error_handler_service.dart @@ -7,7 +7,7 @@ import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; abstract class ErrorHandlerService { - Future handleError(Failure failure); + Future handleError({required Failure failure, Function() onOkPressed}); } class ErrorHandlerServiceImp implements ErrorHandlerService { @@ -22,7 +22,7 @@ class ErrorHandlerServiceImp implements ErrorHandlerService { }); @override - Future handleError(Failure failure) async { + Future handleError({required Failure failure, Function()? onOkPressed}) async { if (failure is APIException) { loggerService.errorLogs("API Exception: ${failure.message}"); } else if (failure is ServerFailure) { @@ -31,6 +31,9 @@ class ErrorHandlerServiceImp implements ErrorHandlerService { } else if (failure is DataParsingFailure) { loggerService.errorLogs("Data Parsing Failure: ${failure.message}"); await _showDialog(failure, title: "Data Error"); + } else if (failure is StatusCodeFailure) { + loggerService.errorLogs("StatusCode Failure: ${failure.message}"); + await _showDialog(failure, title: "Status Code Failure Error"); } else if (failure is HttpException) { loggerService.errorLogs("Http Exception: ${failure.message}"); await _showDialog(failure, title: "Network Error"); @@ -46,7 +49,7 @@ class ErrorHandlerServiceImp implements ErrorHandlerService { } } - Future _showDialog(Failure failure, {String title = "Error"}) async { - await dialogService.showErrorDialog(failure.message); + Future _showDialog(Failure failure, {String title = "Error", Function()? onOkPressed}) async { + await dialogService.showErrorDialog(message: failure.message, onOkPressed: onOkPressed); } } diff --git a/lib/splashPage.dart b/lib/splashPage.dart index 85b2e80..bc3e542 100644 --- a/lib/splashPage.dart +++ b/lib/splashPage.dart @@ -16,8 +16,8 @@ import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; import 'core/consts.dart'; -import 'core/utils/LocalNotification.dart'; -import 'core/utils/push-notification-handler.dart'; +import 'core/utils/local_notifications.dart'; +import 'core/utils/push_notification_handler.dart'; class SplashPage extends StatefulWidget { @override diff --git a/lib/widgets/chip/custom_chip_widget.dart b/lib/widgets/chip/custom_chip_widget.dart index 9afb986..6c2f1e3 100644 --- a/lib/widgets/chip/custom_chip_widget.dart +++ b/lib/widgets/chip/custom_chip_widget.dart @@ -5,7 +5,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; class CustomChipWidget extends StatelessWidget { - final ChipType chipType; + final ChipTypeEnum chipType; final String chipText; final String? iconAsset; final VoidCallback? onTap; @@ -14,7 +14,7 @@ class CustomChipWidget extends StatelessWidget { final EdgeInsetsGeometry padding; const CustomChipWidget({ - Key? key, + super.key, required this.chipType, required this.chipText, this.iconAsset, @@ -22,7 +22,7 @@ class CustomChipWidget extends StatelessWidget { this.isSelected = false, this.borderRadius = 12, this.padding = const EdgeInsets.all(8), - }) : super(key: key); + }); @override Widget build(BuildContext context) { From c6a95fe7d72737c988be1909a6bd760fb9d6a75c Mon Sep 17 00:00:00 2001 From: faizatflutter Date: Tue, 2 Sep 2025 15:18:03 +0300 Subject: [PATCH 4/4] latest push after master merged --- .../authentication_view_model.dart | 182 +++++++++--------- lib/presentation/home/landing_page.dart | 8 +- 2 files changed, 92 insertions(+), 98 deletions(-) diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index 573c528..ed77aca 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -40,96 +40,96 @@ class AuthenticationViewModel extends ChangeNotifier { ); } - Future checkUserAuthentication({Function(dynamic)? onSuccess, Function(String)? onError}) async { - CheckPatientAuthenticationReq checkPatientAuthenticationReq = RequestUtils.getCommonRequestWelcome( - phoneNumber: '0567184134', - otpTypeEnum: OTPTypeEnum.sms, - deviceToken: 'dummyDeviceToken123', - patientOutSA: true, - loginTokenID: 'dummyLoginToken456', - registeredData: null, - patientId: 12345, - nationIdText: '1234567890', - countryCode: 'SA', - ); - - final result = await authenticationRepo.checkPatientAuthentication(checkPatientAuthenticationReq: checkPatientAuthenticationReq); - result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), - (apiResponse) { - if (apiResponse.data['isSMSSent']) { - // TODO: set this in AppState - // sharedPref.setString(LOGIN_TOKEN_ID, value['LogInTokenID']); - // loginTokenID = value['LogInTokenID'], - // sharedPref.setObject(REGISTER_DATA_FOR_LOGIIN, request), - sendActivationCode(type); - } else { - if (apiResponse.data['IsAuthenticated']) { - checkActivationCode(onWrongActivationCode: (String? message) {}); - } - } - }, - ); - } - - Future sendActivationCode({required OTPTypeEnum otpTypeEnum}) async { - var request = RequestUtils.getCommonRequestAuthProvider( - otpTypeEnum: otpTypeEnum, - registeredData: null, - deviceToken: "dummyLoginToken456", - mobileNumber: "0567184134", - zipCode: "SA", - patientOutSA: true, - loginTokenID: "dummyLoginToken456", - selectedOption: selectedOption, - patientId: 12345, - ); + // Future checkUserAuthentication({Function(dynamic)? onSuccess, Function(String)? onError}) async { + // CheckPatientAuthenticationReq checkPatientAuthenticationReq = RequestUtils.getCommonRequestWelcome( + // phoneNumber: '0567184134', + // otpTypeEnum: OTPTypeEnum.sms, + // deviceToken: 'dummyDeviceToken123', + // patientOutSA: true, + // loginTokenID: 'dummyLoginToken456', + // registeredData: null, + // patientId: 12345, + // nationIdText: '1234567890', + // countryCode: 'SA', + // ); + // + // final result = await authenticationRepo.checkPatientAuthentication(checkPatientAuthenticationReq: checkPatientAuthenticationReq); + // result.fold( + // (failure) async => await errorHandlerService.handleError(failure: failure), + // (apiResponse) { + // if (apiResponse.data['isSMSSent']) { + // // TODO: set this in AppState + // // sharedPref.setString(LOGIN_TOKEN_ID, value['LogInTokenID']); + // // loginTokenID = value['LogInTokenID'], + // // sharedPref.setObject(REGISTER_DATA_FOR_LOGIIN, request), + // sendActivationCode(type); + // } else { + // if (apiResponse.data['IsAuthenticated']) { + // checkActivationCode(onWrongActivationCode: (String? message) {}); + // } + // } + // }, + // ); + // } - request.sMSSignature = await SMSOTP.getSignature(); - selectedOption = type; - // GifLoaderDialogUtils.showMyDialog(context); - if (healthId != null || isDubai) { - if (!isDubai) { - request.dob = dob; //isHijri == 1 ? dob : dateFormat2.format(dateFormat.parse(dob)); - } - request.healthId = healthId; - request.isHijri = isHijri; - await this.apiClient.sendActivationCodeRegister(request).then((result) { - // GifLoaderDialogUtils.hideDialog(context); - if (result != null && result['isSMSSent'] == true) { - this.startSMSService(type); - } - }).catchError((r) { - GifLoaderDialogUtils.hideDialog(context); - context.showBottomSheet( - child: ExceptionBottomSheet( - message: r.toString(), - onOkPressed: () { - Navigator.of(context).pop(); - }, - )); - // AppToast.showErrorToast(message: r); - }); - } else { - request.dob = ""; - request.healthId = ""; - request.isHijri = 0; - await this.authService.sendActivationCode(request).then((result) { - GifLoaderDialogUtils.hideDialog(context); - if (result != null && result['isSMSSent'] == true) { - this.startSMSService(type); - } - }).catchError((r) { - GifLoaderDialogUtils.hideDialog(context); - context.showBottomSheet( - child: ExceptionBottomSheet( - message: r.toString(), - onOkPressed: () { - Navigator.of(context).pop(); - }, - )); - // AppToast.showErrorToast(message: r.toString()); - }); - } - } + // Future sendActivationCode({required OTPTypeEnum otpTypeEnum}) async { + // var request = RequestUtils.getCommonRequestAuthProvider( + // otpTypeEnum: otpTypeEnum, + // registeredData: null, + // deviceToken: "dummyLoginToken456", + // mobileNumber: "0567184134", + // zipCode: "SA", + // patientOutSA: true, + // loginTokenID: "dummyLoginToken456", + // selectedOption: selectedOption, + // patientId: 12345, + // ); + // + // request.sMSSignature = await SMSOTP.getSignature(); + // selectedOption = type; + // // GifLoaderDialogUtils.showMyDialog(context); + // if (healthId != null || isDubai) { + // if (!isDubai) { + // request.dob = dob; //isHijri == 1 ? dob : dateFormat2.format(dateFormat.parse(dob)); + // } + // request.healthId = healthId; + // request.isHijri = isHijri; + // await this.apiClient.sendActivationCodeRegister(request).then((result) { + // // GifLoaderDialogUtils.hideDialog(context); + // if (result != null && result['isSMSSent'] == true) { + // this.startSMSService(type); + // } + // }).catchError((r) { + // GifLoaderDialogUtils.hideDialog(context); + // context.showBottomSheet( + // child: ExceptionBottomSheet( + // message: r.toString(), + // onOkPressed: () { + // Navigator.of(context).pop(); + // }, + // )); + // // AppToast.showErrorToast(message: r); + // }); + // } else { + // request.dob = ""; + // request.healthId = ""; + // request.isHijri = 0; + // await this.authService.sendActivationCode(request).then((result) { + // GifLoaderDialogUtils.hideDialog(context); + // if (result != null && result['isSMSSent'] == true) { + // this.startSMSService(type); + // } + // }).catchError((r) { + // GifLoaderDialogUtils.hideDialog(context); + // context.showBottomSheet( + // child: ExceptionBottomSheet( + // message: r.toString(), + // onOkPressed: () { + // Navigator.of(context).pop(); + // }, + // )); + // // AppToast.showErrorToast(message: r.toString()); + // }); + // } + // } } diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 27e95de..8421856 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -11,23 +11,17 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/presentation/authantication/login.dart'; import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/habib_wallet_card.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/small_service_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart'; -import 'package:hmg_patient_app_new/providers/authentication_view_model.dart'; import 'package:hmg_patient_app_new/providers/bottom_navigation_provider.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; -import '../../core/app_assets.dart'; -import '../../core/utils/utils.dart'; -import '../../widgets/buttons/custom_button.dart'; -import '../../widgets/transitions/fade_page.dart'; - class LandingPage extends StatefulWidget { const LandingPage({super.key});