Merge branch 'master' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New into notification_center_id_Dev_sultan
* 'master' of http://34.17.182.140/Haroon6138/HMG_Patient_App_New: (73 commits) updates Updates Dynamic theming has been implemented Completed THE Theme changes theme code updated. MyFatoorah implementation contd. MyFatoorah Payment integration in progress updates updates Updates Update to stores Version 22.1 Updates Locale fix Update to Stores VersionID 22.0 Insurance model updates Habib Wallet & Lakum data updates Habib Wallet & Lakum card updates Dubai timezone handled CMC summary page updated Medical report date fix ...notification_center_id_Dev_sultan
commit
2fcbf71189
@ -0,0 +1,26 @@
|
||||
<resources>
|
||||
<color name="payment_sdk_primary_color">#ffffff</color>
|
||||
<color name="payment_sdk_secondary_color">#ED1C2B</color>
|
||||
<color name="payment_sdk_status_bar_color">#444647</color>
|
||||
<color name="payment_sdk_primary_font_color">#2E3039</color>
|
||||
<color name="payment_sdk_secondary_font_color">#2E3039</color>
|
||||
<color name="payment_sdk_hint_font_color">#a5a5a5</color>
|
||||
<color name="payment_sdk_stroke_color">#e1e1e1</color>
|
||||
<color name="payment_sdk_button_text_color">#FFF</color>
|
||||
<color name="payment_sdk_title_text_color">#1e1e1e</color>
|
||||
<color name="payment_sdk_button_background_color">#ED1C2B</color>
|
||||
<color name="payment_sdk_background_color">#F9FAFD</color>
|
||||
<color name="payment_sdk_blue_F2FAFD">#F2FAFD</color>
|
||||
<color name="payment_sdk_error_text_color">#EC2213</color>
|
||||
<color name="payment_sdk_back_black_dim">#4D6E6E6E</color>
|
||||
<color name="payment_sdk_input_field_background_color">#FFFFFFFF</color>
|
||||
<color name="payment_sdk_enabled_switch_track_color">#00000000</color>
|
||||
<color name="payment_sdk_enabled_switch_handle_color">#3db39e</color>
|
||||
<color name="payment_sdk_disabled_switch_track_color">#00000000</color>
|
||||
<color name="payment_sdk_disabled_switch_handle_color">#c7c7c7</color>
|
||||
<color name="payment_sdk_switch_stroke_color">#4c4c4c</color>
|
||||
<color name="payment_sdk_amount_font_color">#4c4c4c</color>
|
||||
<color name="payment_sdk_original_amount_font_color">#a5a5a5</color>
|
||||
<color name="payment_sdk_billing_header_background_color">#ED1C2B</color>
|
||||
<color name="payment_sdk_billing_text_color">#FFF</color>
|
||||
</resources>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="120" height="120" fill="#EFF1F3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.2503 38.4816C33.2603 37.0472 34.4199 35.8864 35.8543 35.875H83.1463C84.5848 35.875 85.7503 37.0431 85.7503 38.4816V80.5184C85.7403 81.9528 84.5807 83.1136 83.1463 83.125H35.8543C34.4158 83.1236 33.2503 81.957 33.2503 80.5184V38.4816ZM80.5006 41.1251H38.5006V77.8751L62.8921 53.4783C63.9172 52.4536 65.5788 52.4536 66.6039 53.4783L80.5006 67.4013V41.1251ZM43.75 51.6249C43.75 54.5244 46.1005 56.8749 49 56.8749C51.8995 56.8749 54.25 54.5244 54.25 51.6249C54.25 48.7254 51.8995 46.3749 49 46.3749C46.1005 46.3749 43.75 48.7254 43.75 51.6249Z" fill="#687787"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 892 B |
@ -0,0 +1,12 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.9384 3.88371e-06C3.28428 -5.8582e-05 2.69656 -0.000114493 2.22085 0.0638424C1.70149 0.133668 1.16868 0.295802 0.732241 0.732241C0.295802 1.16868 0.133668 1.70149 0.0638424 2.22085C-0.000114493 2.69656 -5.8582e-05 3.28428 3.88371e-06 3.9384L3.88371e-06 4.06161C-5.8582e-05 4.71574 -0.000114493 5.30346 0.0638424 5.77916C0.133668 6.29852 0.295802 6.83134 0.732241 7.26777C1.16868 7.70421 1.70149 7.86635 2.22085 7.93617C2.69656 8.00013 3.28427 8.00007 3.9384 8.00001H4.06162C4.71574 8.00007 5.30346 8.00013 5.77916 7.93617C6.29852 7.86635 6.83134 7.70421 7.26777 7.26777C7.70421 6.83134 7.86635 6.29852 7.93617 5.77916C8.00013 5.30346 8.00007 4.71574 8.00001 4.06162V3.9384C8.00007 3.28427 8.00013 2.69656 7.93617 2.22085C7.86635 1.70149 7.70421 1.16868 7.26777 0.732241C6.83134 0.295802 6.29852 0.133668 5.77916 0.0638424C5.30346 -0.000114493 4.71574 -5.8582e-05 4.06161 3.88371e-06L3.9384 3.88371e-06Z" fill="#8F9AA3"/>
|
||||
<path d="M3.9384 12C3.28428 11.9999 2.69655 11.9999 2.22085 12.0638C1.70149 12.1337 1.16868 12.2958 0.732241 12.7322C0.295802 13.1687 0.133668 13.7015 0.0638424 14.2209C-0.000114493 14.6966 -5.8582e-05 15.2843 3.88371e-06 15.9384L3.88371e-06 16.0616C-5.8582e-05 16.7157 -0.000114493 17.3035 0.0638424 17.7792C0.133668 18.2985 0.295802 18.8313 0.732241 19.2678C1.16868 19.7042 1.70149 19.8663 2.22085 19.9362C2.69656 20.0001 3.28427 20.0001 3.9384 20H4.06162C4.71574 20.0001 5.30346 20.0001 5.77916 19.9362C6.29852 19.8663 6.83134 19.7042 7.26777 19.2678C7.70421 18.8313 7.86635 18.2985 7.93617 17.7792C8.00013 17.3035 8.00007 16.7157 8.00001 16.0616V15.9384C8.00007 15.2843 8.00013 14.6966 7.93617 14.2209C7.86635 13.7015 7.70421 13.1687 7.26777 12.7322C6.83134 12.2958 6.29852 12.1337 5.77916 12.0638C5.30346 11.9999 4.71575 11.9999 4.06163 12H3.9384Z" fill="#8F9AA3"/>
|
||||
<path d="M0 10C0 9.44771 0.447715 9 1 9L7 9C7.55228 9 8 9.44772 8 10C8 10.5523 7.55228 11 7 11L1 11C0.447715 11 0 10.5523 0 10Z" fill="#8F9AA3"/>
|
||||
<path d="M11 1C11 0.447715 10.5523 0 10 0C9.44771 0 9 0.447715 9 1L9 6C9 6.55228 9.44771 7 10 7C10.5523 7 11 6.55228 11 6L11 1Z" fill="#8F9AA3"/>
|
||||
<path d="M15.9384 3.88371e-06L16.0616 3.88371e-06C16.7157 -5.8582e-05 17.3035 -0.000114493 17.7792 0.0638424C18.2985 0.133668 18.8313 0.295802 19.2678 0.732241C19.7042 1.16868 19.8663 1.70149 19.9362 2.22085C20.0001 2.69656 20.0001 3.28427 20 3.9384V4.06162C20.0001 4.71574 20.0001 5.30346 19.9362 5.77916C19.8663 6.29852 19.7042 6.83134 19.2678 7.26777C18.8313 7.70421 18.2985 7.86635 17.7792 7.93617C17.3035 8.00013 16.7157 8.00007 16.0616 8.00001H15.9384C15.2843 8.00007 14.6966 8.00013 14.2209 7.93617C13.7015 7.86635 13.1687 7.70421 12.7322 7.26777C12.2958 6.83134 12.1337 6.29852 12.0638 5.77916C11.9999 5.30346 11.9999 4.71575 12 4.06163V3.9384C11.9999 3.28428 11.9999 2.69655 12.0638 2.22085C12.1337 1.70149 12.2958 1.16868 12.7322 0.732241C13.1687 0.295802 13.7015 0.133668 14.2209 0.0638424C14.6966 -0.000114493 15.2843 -5.8582e-05 15.9384 3.88371e-06Z" fill="#8F9AA3"/>
|
||||
<path d="M12.9384 9C12.2843 8.99994 11.6966 8.99989 11.2209 9.06384C10.7015 9.13367 10.1687 9.2958 9.73224 9.73224C9.2958 10.1687 9.13367 10.7015 9.06384 11.2209C8.99989 11.6966 8.99994 12.2843 9 12.9384L9.00001 13C9.00001 13.5523 9.44772 14 10 14C10.5523 14 11 13.5523 11 13C11 12.2646 11.0021 11.8137 11.046 11.4873C11.0846 11.2005 11.1394 11.1526 11.1459 11.1469L11.1469 11.1459C11.1526 11.1394 11.2005 11.0846 11.4873 11.046C11.8137 11.0021 12.2646 11 13 11L19 11C19.5523 11 20 10.5523 20 10C20 9.44772 19.5523 9.00001 19 9.00001L12.9384 9Z" fill="#8F9AA3"/>
|
||||
<path d="M16 14C16.7354 14 17.1862 14.0021 17.5125 14.0461C17.799 14.0846 17.8468 14.1395 17.8526 14.146L17.8536 14.1471C17.8604 14.1531 17.9154 14.2016 17.954 14.4891C17.9979 14.8162 18 15.268 18 16.0043C18 16.7407 17.9979 17.1925 17.954 17.5196C17.9154 17.8071 17.8604 17.8556 17.8536 17.8616L17.8522 17.863C17.8442 17.8712 17.8181 17.8979 17.7034 17.9272C17.5612 17.9636 17.3416 17.9892 16.9698 18.0005C16.4178 18.0171 15.9838 18.4781 16.0005 19.0302C16.0171 19.5822 16.4781 20.0162 17.0302 19.9996C17.7921 19.9766 18.64 19.9045 19.2683 19.2753C19.7045 18.8385 19.8664 18.3054 19.9362 17.7857C20.0001 17.3095 20.0001 16.7211 20 16.066V15.9428C20.0001 15.2876 20.0001 14.6992 19.9362 14.223C19.8664 13.7033 19.7045 13.1702 19.2683 12.7334C18.8319 12.2964 18.299 12.1339 17.7794 12.0639C17.3036 11.9999 16.7157 11.9999 16.0617 12L16 12C15.4477 12 15 12.4477 15 13C15 13.5523 15.4477 14 16 14Z" fill="#8F9AA3"/>
|
||||
<path d="M14 13C14 12.4477 13.5523 12 13 12C12.4477 12 12 12.4477 12 13V14.5C12 15.394 12.2542 16.2178 13.0565 16.6656C13.3946 16.8543 13.7549 16.928 14.0621 16.9627C14.3698 16.9975 14.6995 17 15 17C15 17.5523 15.4477 18 16 18C16.5523 18 17 17.5523 17 17C17 15.8954 16.1046 15 15 15C14.6924 15 14.468 14.9958 14.2866 14.9753C14.152 14.9601 14.0822 14.9397 14.051 14.9279C14.0333 14.8814 14 14.7576 14 14.5V13Z" fill="#8F9AA3"/>
|
||||
<path d="M11 15.7692C11 15.217 10.5523 14.7692 10 14.7692C9.44772 14.7692 9.00001 15.217 9.00001 15.7692L9.00001 18.5385C9.00001 19.0908 9.44772 19.5385 10 19.5385C10.5523 19.5385 11 19.0908 11 18.5385L11 15.7692Z" fill="#8F9AA3"/>
|
||||
<path d="M13 18C12.4477 18 12 18.4477 12 19C12 19.5523 12.4477 20 13 20H14C14.5523 20 15 19.5523 15 19C15 18.4477 14.5523 18 14 18H13Z" fill="#8F9AA3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
@ -0,0 +1,7 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
|
||||
<g transform="translate(8.8, 8.8) scale(1.6)">
|
||||
<path d="M11.5658 1.95717C9.82689 0.890531 8.26692 1.31563 7.32452 2.02335C7.26938 2.06476 7.22039 2.10153 7.17659 2.13413C7.08676 2.20099 7.04185 2.23442 6.98387 2.23442C6.92589 2.23442 6.88098 2.20099 6.79115 2.13413C6.74735 2.10153 6.69836 2.06476 6.64322 2.02335C5.70082 1.31563 4.14085 0.890531 2.40195 1.95717C1.53619 2.48823 0.943044 3.43564 0.767402 4.5853C0.741775 4.75304 0.728961 4.83691 0.781341 4.89789C0.83372 4.95888 0.923033 4.95887 1.10166 4.95885L3.00095 4.95868C3.24133 4.95767 3.64619 4.95596 4.03964 5.0991C4.19392 5.15522 4.33336 5.22877 4.4573 5.3094C4.63656 5.42602 4.72618 5.48433 4.79864 5.46761C4.87109 5.45089 4.92018 5.36908 5.01835 5.20545L5.44147 4.50026C5.71062 4.05168 6.19943 3.78162 6.72246 3.79256C7.24549 3.80349 7.72259 4.09374 7.97275 4.55319L8.72948 5.94294C8.77795 6.03196 8.80218 6.07647 8.8435 6.10102C8.88482 6.12557 8.9355 6.12557 9.03686 6.12557H9.31698C10.1224 6.12557 10.7753 6.77849 10.7753 7.5839C10.7753 8.38932 10.1224 9.04224 9.31698 9.04224L8.66574 9.04348C8.42373 9.04908 7.69714 9.06588 7.06184 8.57794C7.0432 8.56363 7.02501 8.54906 7.00725 8.5343C6.81047 8.37075 6.71208 8.28898 6.63276 8.30247C6.55344 8.31596 6.49635 8.41111 6.38218 8.6014L6.19249 8.91754C5.93991 9.33851 5.49248 9.60414 5.00197 9.62434C4.51146 9.64453 4.0437 9.41658 3.75738 9.0178L3.04199 8.02146C2.99064 7.94995 2.96497 7.91419 2.92736 7.8949C2.88975 7.8756 2.84573 7.8756 2.75769 7.8756L1.88603 7.87559C1.63453 7.87558 1.50878 7.87558 1.45819 7.96269C1.40759 8.0498 1.46788 8.15548 1.58846 8.36685C2.2106 9.45742 3.18686 10.5632 4.58832 11.6005C5.43495 12.2275 6.05634 12.6877 6.98387 12.6877C7.91141 12.6877 8.53279 12.2275 9.37943 11.6005C11.8175 9.79586 12.9688 7.78416 13.2073 6.00548C13.4447 4.23535 12.7682 2.69472 11.5658 1.95717Z" fill="#8F9AA3"/>
|
||||
<path d="M7.20469 4.97105C7.10463 4.78727 6.91379 4.67117 6.70458 4.66679C6.49537 4.66242 6.29984 4.77044 6.19218 4.94988L4.8987 7.10568L4.50819 6.5618C4.50002 6.55043 4.49145 6.53935 4.48248 6.52859C4.47338 6.51766 4.4634 6.50547 4.45255 6.49222C4.32787 6.33991 4.08822 6.04716 3.74091 5.92081C3.49697 5.83206 3.23003 5.83265 2.965 5.83324L2.90072 5.83333H1.73405C1.41188 5.83333 1.15072 6.0945 1.15072 6.41667C1.15072 6.73883 1.41188 7 1.73405 7H2.90072C3.27245 7 3.31696 7.00823 3.34111 7.01684C3.34329 7.01792 3.36229 7.02776 3.40145 7.06546C3.45005 7.11226 3.4979 7.16947 3.57276 7.25931L4.46854 8.50689C4.58307 8.6664 4.77018 8.75758 4.96638 8.74951C5.16258 8.74143 5.34156 8.63518 5.44259 8.46679L6.66782 6.42473L7.16648 7.34054C7.17206 7.35078 7.17794 7.36086 7.18412 7.37075L7.20842 7.40983C7.30282 7.56206 7.41744 7.74688 7.59523 7.88343C7.98366 8.18176 8.4448 8.17225 8.67006 8.16761C8.69423 8.16711 8.71569 8.16667 8.73405 8.16667H9.31738C9.63955 8.16667 9.90072 7.9055 9.90072 7.58333C9.90072 7.26117 9.63955 7 9.31738 7H8.73405C8.69863 7 8.66723 7.00017 8.63892 7.00032C8.53614 7.00086 8.47421 7.00119 8.40933 6.99253C8.35288 6.98499 8.32768 6.97346 8.31135 6.96218C8.30864 6.95892 8.30403 6.95313 8.29726 6.94384C8.27206 6.90926 8.24113 6.86063 8.18271 6.76722L7.20469 4.97105Z" fill="#8F9AA3"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
@ -0,0 +1,12 @@
|
||||
{
|
||||
"key": "none",
|
||||
"enabled": false,
|
||||
"start_date": null,
|
||||
"end_date": null,
|
||||
"primary_color": null,
|
||||
"secondary_color": null,
|
||||
"background_opacity_image": null,
|
||||
"background_image_opacity": 0.2,
|
||||
"header_image": null
|
||||
}
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
class MyFatoorahConfig {
|
||||
|
||||
static const String configAPIKey = "SK_KWT_0auB76h3Frhj7aUBSZa6H5ei792eZoGAPh0bBxKsrgtyjqKgf38MPnZqzGqagErb";
|
||||
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
import 'package:freerasp/freerasp.dart';
|
||||
|
||||
final talsecConfig = TalsecConfig(
|
||||
androidConfig: AndroidConfig(
|
||||
packageName: 'com.cloudsolutions.HMGPatientApp',
|
||||
signingCertHashes: ['6tvWaoN5coG4SnfxGbdQlcLmM0J4ePQwDjrKIg+QkV0=', 'j6VEqVhrypHMIiXiFdRLDdGwjGaMGWY7KAdBJA+Z4Pc='], // Must be the release cert hash
|
||||
supportedStores: ['com.android.vending'], // Google Play Store
|
||||
),
|
||||
iosConfig: IOSConfig(
|
||||
bundleIds: ['com.cloudsolutions.HMGPatientApp'], // iOS Bundle ID
|
||||
teamId: '3A359E86ZF', // Found in Apple Developer portal
|
||||
),
|
||||
watcherMail: '', // Required to receive security alerts
|
||||
isProd: true, // Enforces strict checks for release builds
|
||||
);
|
||||
@ -0,0 +1,197 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
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/cache_consts.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||
import 'package:hmg_patient_app_new/core/myFatoorah_config.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/features/payfort/models/payfort_request_insert_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/payfort/models/payfort_response_insert_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart';
|
||||
import 'package:hmg_patient_app_new/generated/codegen_loader.g.dart';
|
||||
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
|
||||
import 'package:myfatoorah_flutter/myfatoorah_flutter.dart';
|
||||
|
||||
class MyFatoorahViewModel extends ChangeNotifier {
|
||||
final AppState appState;
|
||||
final ErrorHandlerService errorHandlerService;
|
||||
final PayfortRepo payfortRepo;
|
||||
|
||||
MyFatoorahViewModel({
|
||||
required this.appState,
|
||||
required this.errorHandlerService,
|
||||
required this.payfortRepo,
|
||||
});
|
||||
|
||||
// Payment Method ID
|
||||
// 2: Credit/Debit Card
|
||||
// 6: Mada
|
||||
|
||||
initiateCardPayment({
|
||||
num amount = 10.0,
|
||||
paymentDescription = "",
|
||||
String displayCurrencyIso = MFCurrencyISO.KUWAIT_KWD,
|
||||
required String paymentOption,
|
||||
required String liveServiceID,
|
||||
required String isScheduleLiveCare,
|
||||
required String appointmentDate,
|
||||
required String appointmentNo,
|
||||
required String doctorID,
|
||||
required String fileNumber,
|
||||
required String serviceID,
|
||||
required String projectID,
|
||||
Function(MFGetPaymentStatusResponse)? onSuccess,
|
||||
Function(String)? onError,
|
||||
}) async {
|
||||
LoaderBottomSheet.showLoader();
|
||||
await MFSDK.init(MyFatoorahConfig.configAPIKey, ApiConsts.myFatoorahCountry, ApiConsts.myFatoorahEnvironmentType);
|
||||
|
||||
MFSDK.setUpActionBar(toolBarTitle: paymentDescription, toolBarTitleColor: "#ffffff", toolBarBackgroundColor: '#ED1C2B', isShowToolBar: true);
|
||||
|
||||
var request = MFExecutePaymentRequest(paymentMethodId: paymentOption.toLowerCase() == "mada" ? 6 : 2, invoiceValue: amount);
|
||||
request.displayCurrencyIso = displayCurrencyIso;
|
||||
request.customerName = ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? "Test test" : "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}";
|
||||
request.customerEmail = "CustID_${appState.getAuthenticatedUser()!.patientId}@HMG.com";
|
||||
|
||||
await MFSDK.executePayment(request, appState.isArabic() ? MFLanguage.ARABIC : MFLanguage.ENGLISH, (invoiceId) async {
|
||||
PayfortRequestInsertModel payfortRequestInsertModel = PayfortRequestInsertModel();
|
||||
|
||||
payfortRequestInsertModel.commandType = "SALE";
|
||||
payfortRequestInsertModel.amount = amount;
|
||||
payfortRequestInsertModel.customerEmail = getIt.get<AppState>().getAuthenticatedUser()!.emailAddress;
|
||||
payfortRequestInsertModel.orderDescription = paymentDescription;
|
||||
payfortRequestInsertModel.serviceID = serviceID;
|
||||
payfortRequestInsertModel.paymentOption = paymentOption;
|
||||
payfortRequestInsertModel.clientRequestID = invoiceId;
|
||||
payfortRequestInsertModel.projectID = projectID;
|
||||
payfortRequestInsertModel.fileNumber = getIt.get<AppState>().getAuthenticatedUser()!.patientId.toString();
|
||||
payfortRequestInsertModel.customerName = "${getIt.get<AppState>().getAuthenticatedUser()!.firstName} ${getIt.get<AppState>().getAuthenticatedUser()!.lastName}";
|
||||
payfortRequestInsertModel.currency = getIt.get<AppState>().getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED";
|
||||
payfortRequestInsertModel.liveServiceID = liveServiceID;
|
||||
payfortRequestInsertModel.latitude = getIt.get<AppState>().userLat.toString();
|
||||
payfortRequestInsertModel.longitude = getIt.get<AppState>().userLong.toString();
|
||||
payfortRequestInsertModel.merchantReference = invoiceId;
|
||||
payfortRequestInsertModel.deviceToken = await Utils.getStringFromPrefs(CacheConst.pushToken);
|
||||
payfortRequestInsertModel.language = getIt.get<AppState>().isArabic() ? "ar" : "en";
|
||||
payfortRequestInsertModel.integrationId = "6"; // PayTabs integration ID
|
||||
payfortRequestInsertModel.isSchedule = isScheduleLiveCare;
|
||||
payfortRequestInsertModel.appointmentDate = appointmentDate;
|
||||
payfortRequestInsertModel.appointmentNo = appointmentNo;
|
||||
payfortRequestInsertModel.doctorID = doctorID;
|
||||
|
||||
// Call request insert API before payment
|
||||
await paytabsRequestInsert(payfortRequestInsertModel: payfortRequestInsertModel);
|
||||
|
||||
LoaderBottomSheet.hideLoader();
|
||||
}).then((value) async {
|
||||
var valJson = value.toString();
|
||||
print(valJson);
|
||||
|
||||
// // Create PayfortResponseInsertModel (same as Payfort implementation)
|
||||
PayfortResponseInsertModel payfortResponseInsertModel = PayfortResponseInsertModel();
|
||||
|
||||
payfortResponseInsertModel.fortId = value.invoiceTransactions!.first.transactionId ?? "";
|
||||
payfortResponseInsertModel.commandType = "SALE";
|
||||
// payfortResponseInsertModel.amount = paytabsTransactionResponseModel.tranTotal ?? 0;
|
||||
payfortResponseInsertModel.amount = amount;
|
||||
payfortResponseInsertModel.sadadOLP = "";
|
||||
payfortResponseInsertModel.paymentOption = value.invoiceTransactions!.first.paymentGateway ?? "";
|
||||
payfortResponseInsertModel.customerIP = "";
|
||||
payfortResponseInsertModel.eCI = "";
|
||||
payfortResponseInsertModel.responseMessage = value.invoiceTransactions!.first.transactionStatus ?? "";
|
||||
payfortResponseInsertModel.cardNumber = value.invoiceTransactions!.first.cardNumber ?? "";
|
||||
payfortResponseInsertModel.cardBin = "";
|
||||
payfortResponseInsertModel.installmentsNumber = 0;
|
||||
payfortResponseInsertModel.status = (value.invoiceStatus ?? "").toLowerCase() == "paid" ? "14" : "00";
|
||||
payfortResponseInsertModel.merchantRef = value.invoiceId.toString();
|
||||
payfortResponseInsertModel.isRefund = false;
|
||||
payfortResponseInsertModel.remmeberMe = true;
|
||||
payfortResponseInsertModel.reconciliationReference = "";
|
||||
|
||||
// Call response insert API after payment
|
||||
await paytabsResponseInsert(payfortResponseInsertModel: payfortResponseInsertModel);
|
||||
|
||||
if ((value.invoiceStatus ?? "").toLowerCase() == "paid") {
|
||||
if (onSuccess != null) {
|
||||
onSuccess(value);
|
||||
}
|
||||
} else {
|
||||
if (onError != null) {
|
||||
onError(LocaleKeys.paymentFailedPleaseTryAgain.tr());
|
||||
}
|
||||
}
|
||||
}).catchError((error) {
|
||||
if (onError != null) {
|
||||
onError(error.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executeApplePayPayment() {
|
||||
MFApplepay.executeApplePayPayment(onInvoiceCreated: (invoiceID) {
|
||||
log(
|
||||
invoiceID.toString(),
|
||||
);
|
||||
}).then((value) {
|
||||
log(
|
||||
value.toString(),
|
||||
);
|
||||
}).catchError((error) {
|
||||
log(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
initApplePayPayment(num invoiceValue) async {
|
||||
await MFSDK.init(MyFatoorahConfig.configAPIKey, ApiConsts.myFatoorahCountry, ApiConsts.myFatoorahEnvironmentType);
|
||||
|
||||
await MFSDK.initSession(MFInitiateSessionRequest(), MFLanguage.ENGLISH).then((value) async {
|
||||
var executePaymentRequest = MFExecutePaymentRequest(invoiceValue: invoiceValue, sessionId: value.sessionId, displayCurrencyIso: MFCurrencyISO.SAUDIARABIA_SAR);
|
||||
|
||||
MFApplepay.setupApplePay(value, executePaymentRequest, MFLanguage.ENGLISH, merchantName: "Dr Sulaiman Al Habib");
|
||||
}).catchError((error) {
|
||||
log(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> paytabsRequestInsert({required PayfortRequestInsertModel payfortRequestInsertModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
|
||||
final result = await payfortRepo.payfortRequestInsert(payfortRequestInsertModel: payfortRequestInsertModel);
|
||||
|
||||
result.fold(
|
||||
(failure) async => await errorHandlerService.handleError(failure: failure),
|
||||
(apiResponse) {
|
||||
if (apiResponse.messageStatus == 2) {
|
||||
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
|
||||
} else if (apiResponse.messageStatus == 1) {
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(apiResponse);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> paytabsResponseInsert({required PayfortResponseInsertModel payfortResponseInsertModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
|
||||
final result = await payfortRepo.payfortResponseInsert(payfortResponseInsertModel: payfortResponseInsertModel);
|
||||
|
||||
result.fold(
|
||||
(failure) async => await errorHandlerService.handleError(failure: failure),
|
||||
(apiResponse) {
|
||||
if (apiResponse.messageStatus == 2) {
|
||||
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
|
||||
} else if (apiResponse.messageStatus == 1) {
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(apiResponse);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Model representing a remotely-configurable "occasion" theme
|
||||
/// (e.g. Saudi National Day, Founding Day, Eid, Ramadan, ...).
|
||||
///
|
||||
/// Everything here is fully data-driven — nothing about a specific
|
||||
/// occasion (name, color, dates, banner asset) is hardcoded in the app.
|
||||
/// All values are supplied via Firebase Remote Config (or the bundled
|
||||
/// default JSON fallback used before the first successful fetch).
|
||||
@immutable
|
||||
class OccasionConfigModel {
|
||||
/// Unique identifier of the occasion (e.g. "national_day"). Used only
|
||||
/// for logging/analytics — no app logic branches on this value.
|
||||
final String key;
|
||||
|
||||
/// Master kill-switch. When false, the app must look exactly like the
|
||||
/// default/current theme, regardless of any other field below.
|
||||
final bool enabled;
|
||||
|
||||
/// Optional activation window. When both are present, the theme is only
|
||||
/// considered active if `DateTime.now()` falls within [startDate, endDate].
|
||||
/// When null, the [enabled] flag alone controls activation.
|
||||
final DateTime? startDate;
|
||||
final DateTime? endDate;
|
||||
|
||||
/// Hex string (e.g. "#00894A") for the occasion's primary brand color.
|
||||
/// When null, the app's default primary color is preserved.
|
||||
final String? primaryColorHex;
|
||||
|
||||
/// Remote (or bundled asset path) image rendered as a decorative
|
||||
/// background/watermark, drawn using [backgroundImageOpacity].
|
||||
final String? backgroundOpacityImage;
|
||||
|
||||
/// Opacity (0.0 - 1.0) applied to [backgroundOpacityImage].
|
||||
final double backgroundImageOpacity;
|
||||
|
||||
/// Remote (or bundled asset path) image rendered as the occasion's main
|
||||
/// header/banner (e.g. the National Day greeting banner), drawn at full
|
||||
/// opacity on top of [backgroundOpacityImage].
|
||||
final String? headerImage;
|
||||
|
||||
const OccasionConfigModel({
|
||||
required this.key,
|
||||
required this.enabled,
|
||||
this.startDate,
|
||||
this.endDate,
|
||||
this.primaryColorHex,
|
||||
this.backgroundOpacityImage,
|
||||
this.backgroundImageOpacity = 1.0,
|
||||
this.headerImage,
|
||||
});
|
||||
|
||||
/// Fully inert config — identical to the app's current/default look.
|
||||
factory OccasionConfigModel.disabled() => const OccasionConfigModel(
|
||||
key: 'none',
|
||||
enabled: false,
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
primaryColorHex: null,
|
||||
backgroundOpacityImage: null,
|
||||
backgroundImageOpacity: 1.0,
|
||||
headerImage: null,
|
||||
);
|
||||
|
||||
factory OccasionConfigModel.fromJson(Map<String, dynamic> json) {
|
||||
return OccasionConfigModel(
|
||||
key: (json['key'] as String?) ?? 'none',
|
||||
enabled: (json['enabled'] as bool?) ?? false,
|
||||
startDate: _parseDate(json['start_date']),
|
||||
endDate: _parseDate(json['end_date']),
|
||||
primaryColorHex: json['primary_color'] as String?,
|
||||
backgroundOpacityImage: json['background_opacity_image'] as String?,
|
||||
backgroundImageOpacity: (json['background_image_opacity'] as num?)?.toDouble() ?? 1.0,
|
||||
headerImage: json['header_image'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
static DateTime? _parseDate(dynamic value) {
|
||||
if (value == null) return null;
|
||||
if (value is String && value.isNotEmpty) {
|
||||
return DateTime.tryParse(value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Whether "now" falls inside the configured activation window.
|
||||
/// If no window is configured, the [enabled] flag alone decides.
|
||||
bool isWithinActiveWindow([DateTime? now]) {
|
||||
final current = now ?? DateTime.now();
|
||||
if (startDate != null && current.isBefore(startDate!)) return false;
|
||||
if (endDate != null && current.isAfter(endDate!)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Parsed [Color] from [primaryColorHex], or null if not configured.
|
||||
Color? get primaryColor => _hexToColor(primaryColorHex);
|
||||
|
||||
/// Lighter/translucent variant of [primaryColor] — 11% opacity of the same
|
||||
/// hue (e.g. for chip/badge backgrounds). Derived automatically from
|
||||
/// [primaryColor]; not an independent remote-config field. Null unless
|
||||
/// [primaryColor] is configured.
|
||||
Color? get secondaryColor => primaryColor?.withValues(alpha: 0.11);
|
||||
|
||||
static Color? _hexToColor(String? hex) {
|
||||
if (hex == null || hex.isEmpty) return null;
|
||||
var value = hex.trim().replaceFirst('#', '');
|
||||
if (value.length == 6) value = 'FF$value';
|
||||
final parsed = int.tryParse(value, radix: 16);
|
||||
return parsed != null ? Color(parsed) : null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'key': key,
|
||||
'enabled': enabled,
|
||||
'start_date': startDate?.toIso8601String(),
|
||||
'end_date': endDate?.toIso8601String(),
|
||||
'primary_color': primaryColorHex,
|
||||
'background_opacity_image': backgroundOpacityImage,
|
||||
'background_image_opacity': backgroundImageOpacity,
|
||||
'header_image': headerImage,
|
||||
};
|
||||
}
|
||||
|
||||
@ -0,0 +1,109 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/painting.dart' show Color;
|
||||
import 'package:hmg_patient_app_new/features/occasion_theme/occasion_config_model.dart';
|
||||
import 'package:hmg_patient_app_new/services/cache_service.dart';
|
||||
import 'package:hmg_patient_app_new/services/logger_service.dart';
|
||||
import 'package:hmg_patient_app_new/services/remote_config_service.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
|
||||
/// Drives the fully remote-config-based "occasion" theming (National Day,
|
||||
/// Founding Day, Eid, Ramadan, ...). Nothing about a specific occasion is
|
||||
/// hardcoded here — every color/asset/date comes from [OccasionConfigModel],
|
||||
/// which is parsed from Firebase Remote Config.
|
||||
///
|
||||
/// Mirrors the existing dark-mode pattern in ProfileSettingsViewModel:
|
||||
/// a static flag on [AppColors] is kept in sync so every widget using
|
||||
/// AppColors.primaryRedColor (even ones with no BuildContext/Provider
|
||||
/// access) reflects the active occasion instantly.
|
||||
class OccasionThemeViewModel extends ChangeNotifier {
|
||||
static const String _cachedConfigKey = 'cached_occasion_config_json';
|
||||
|
||||
final RemoteConfigService _remoteConfigService;
|
||||
final CacheService _cacheService;
|
||||
final LoggerService _loggerService;
|
||||
|
||||
OccasionThemeViewModel({
|
||||
required RemoteConfigService remoteConfigService,
|
||||
required CacheService cacheService,
|
||||
required LoggerService loggerService,
|
||||
}) : _remoteConfigService = remoteConfigService,
|
||||
_cacheService = cacheService,
|
||||
_loggerService = loggerService;
|
||||
|
||||
OccasionConfigModel _config = OccasionConfigModel.disabled();
|
||||
OccasionConfigModel get config => _config;
|
||||
|
||||
/// True only when enabled AND (if a window is configured) within it.
|
||||
bool get isOccasionActive => _config.enabled && _config.isWithinActiveWindow();
|
||||
|
||||
String? get backgroundOpacityImage => isOccasionActive ? _config.backgroundOpacityImage : null;
|
||||
|
||||
double get backgroundImageOpacity => _config.backgroundImageOpacity;
|
||||
|
||||
/// The occasion's main header/banner image (e.g. National Day greeting
|
||||
/// banner), rendered at full opacity. Null unless an occasion is active.
|
||||
String? get headerImage => isOccasionActive ? _config.headerImage : null;
|
||||
|
||||
/// Lighter/translucent variant of the active occasion's primary color
|
||||
/// (11% opacity) — e.g. for chip/badge backgrounds. Null unless an
|
||||
/// occasion is active.
|
||||
Color? get secondaryColor => isOccasionActive ? _config.secondaryColor : null;
|
||||
|
||||
/// Call once at app startup (before the first frame) to restore the last
|
||||
/// known-good config instantly (offline-safe), then kick off a remote
|
||||
/// fetch in the background.
|
||||
Future<void> initialize() async {
|
||||
_restoreCachedConfig();
|
||||
_applyToAppColors();
|
||||
|
||||
try {
|
||||
await _remoteConfigService.initialize();
|
||||
await refresh();
|
||||
} catch (ex) {
|
||||
_loggerService.logError('OccasionThemeViewModel.initialize error: $ex');
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetches the latest occasion config from Firebase Remote Config and
|
||||
/// applies it. Safe to call again later (e.g. on app resume) to pick up
|
||||
/// changes without requiring an app update.
|
||||
Future<void> refresh() async {
|
||||
try {
|
||||
await _remoteConfigService.fetchAndActivate();
|
||||
final rawJson = _remoteConfigService.occasionConfigRawJson;
|
||||
_config = OccasionConfigModel.fromJson(json.decode(rawJson) as Map<String, dynamic>);
|
||||
await _cacheService.saveString(key: _cachedConfigKey, value: rawJson);
|
||||
_applyToAppColors();
|
||||
notifyListeners();
|
||||
} catch (ex) {
|
||||
_loggerService.logError('OccasionThemeViewModel.refresh error: $ex');
|
||||
// Keep whatever config was already applied (cached/disabled) — never
|
||||
// let a remote-config failure change the app's appearance unexpectedly.
|
||||
}
|
||||
}
|
||||
|
||||
void _restoreCachedConfig() {
|
||||
try {
|
||||
final cachedJson = _cacheService.getString(key: _cachedConfigKey);
|
||||
if (cachedJson == null || cachedJson.isEmpty) {
|
||||
_config = OccasionConfigModel.disabled();
|
||||
return;
|
||||
}
|
||||
_config = OccasionConfigModel.fromJson(json.decode(cachedJson) as Map<String, dynamic>);
|
||||
} catch (ex) {
|
||||
_loggerService.logError('OccasionThemeViewModel._restoreCachedConfig error: $ex');
|
||||
_config = OccasionConfigModel.disabled();
|
||||
}
|
||||
}
|
||||
|
||||
/// Keeps AppColors in sync so non-Provider call sites (AppColors.primaryRedColor)
|
||||
/// pick up the occasion color exactly like the existing dark-mode flag does.
|
||||
void _applyToAppColors() {
|
||||
AppColors.isOccasionThemeActive = isOccasionActive;
|
||||
AppColors.occasionPrimaryColor = _config.primaryColor;
|
||||
AppColors.occasionSecondaryColor = _config.secondaryColor;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,230 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.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/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
|
||||
import '../../../generated/locale_keys.g.dart';
|
||||
|
||||
class LiveCareTermsConditions extends StatelessWidget {
|
||||
const LiveCareTermsConditions({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isArabic = getIt.get<AppState>().isArabic();
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.scaffoldBgColor,
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CollapsingListView(
|
||||
title: LocaleKeys.livecare.tr(context: context),
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 16.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsIntroHeading.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions1.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions2.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading2.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions3.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions3a.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions3b.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions3c.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions3d.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions4.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions5.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions6.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading3.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions7.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions8.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions9.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions10.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions11.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions12.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions13.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading4.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions14.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions15.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions16.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading5.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions17.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions18.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions19.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions19a.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions19b.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions19c.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions19d.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions19e.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions20.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading6.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions21.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions22.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions23.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading7.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions24.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions25.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions26.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions27.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions28.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions29.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions30.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading8.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions31.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading9.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions32.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions33.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions34.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions35.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading10.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions36.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading11.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions37.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions38.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading12.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions39.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions40.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions41.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading13.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions42.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading14.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43a.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43b.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43c.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43d.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43e.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43f.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43g.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading15.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions44.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions45.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions46.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading16.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions47.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions48.tr(context: context), isArabic),
|
||||
SizedBox(height: 24.h),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Helper method to build heading text with consistent styling
|
||||
Widget _buildHeading(BuildContext context, String text, bool isArabic) {
|
||||
return Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 20.f,
|
||||
fontFamily: isArabic ? 'CairoArabic' : 'Poppins',
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textColor,
|
||||
letterSpacing: -0.5,
|
||||
height: 1.4,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Helper method to build content text with consistent styling
|
||||
Widget _buildContent(BuildContext context, String text, bool isArabic) {
|
||||
return Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 16.f,
|
||||
fontFamily: isArabic ? 'CairoArabic' : 'Poppins',
|
||||
color: AppColors.textColor,
|
||||
letterSpacing: -0.3,
|
||||
height: 1.5,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,97 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/occasion_theme/occasion_theme_view_model.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
/// Renders the occasion's main header/banner (`header_image`, e.g. the
|
||||
/// National Day greeting banner) inline at the top of the landing page's
|
||||
/// scrollable content, drawn at full opacity.
|
||||
///
|
||||
/// Fully data-driven: when no occasion is active (the default), this widget
|
||||
/// renders nothing and takes zero space — the existing layout/UI is
|
||||
/// completely unaffected.
|
||||
///
|
||||
/// See also [OccasionBackgroundWidget], which renders the decorative
|
||||
/// backdrop image as a full-page background instead of inline content.
|
||||
class OccasionBannerWidget extends StatelessWidget {
|
||||
const OccasionBannerWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<OccasionThemeViewModel>(
|
||||
builder: (context, occasionThemeVm, _) {
|
||||
final headerImageUrl = occasionThemeVm.headerImage;
|
||||
final hasHeaderImage = occasionThemeVm.isOccasionActive && (headerImageUrl?.isNotEmpty ?? false);
|
||||
|
||||
if (!hasHeaderImage) {
|
||||
return const SizedBox(height: 0);
|
||||
}
|
||||
|
||||
return buildOccasionImage(headerImageUrl!).paddingOnly(bottom: 16.h);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the occasion's decorative backdrop/watermark
|
||||
/// (`background_opacity_image`) as a full-page background. Place this as
|
||||
/// the first child of the landing page's outer [Stack] (behind the
|
||||
/// scrollable content) so it fills the whole screen and sits behind
|
||||
/// everything else, drawn at [OccasionThemeViewModel.backgroundImageOpacity].
|
||||
///
|
||||
/// Fully data-driven: when no occasion is active (the default), this widget
|
||||
/// renders nothing — the existing layout/UI is completely unaffected.
|
||||
class OccasionBackgroundWidget extends StatelessWidget {
|
||||
const OccasionBackgroundWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<OccasionThemeViewModel>(
|
||||
builder: (context, occasionThemeVm, _) {
|
||||
final backgroundImageUrl = occasionThemeVm.backgroundOpacityImage;
|
||||
final hasBackgroundImage = occasionThemeVm.isOccasionActive && (backgroundImageUrl?.isNotEmpty ?? false);
|
||||
|
||||
if (!hasBackgroundImage) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Positioned.fill(
|
||||
child: Opacity(
|
||||
opacity: occasionThemeVm.backgroundImageOpacity.clamp(0.0, 1.0),
|
||||
child: buildOccasionImage(backgroundImageUrl!, isNeedPadding: true),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Remote-config image URLs may point to either an SVG (e.g. the National
|
||||
/// Day banner) or a raster image (PNG/JPG). [CachedNetworkImage] cannot
|
||||
/// decode SVG — it silently renders nothing — so SVG URLs are routed to
|
||||
/// [SvgPicture.network] instead, based on the URL's file extension.
|
||||
Widget buildOccasionImage(String url, {bool isNeedPadding = false}) {
|
||||
final isSvg = url.split('?').first.toLowerCase().endsWith('.svg');
|
||||
if (isSvg) {
|
||||
return SvgPicture.network(
|
||||
url,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.contain,
|
||||
placeholderBuilder: (_) => const SizedBox.shrink(),
|
||||
);
|
||||
}
|
||||
return CachedNetworkImage(
|
||||
imageUrl: url,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.contain,
|
||||
placeholder: (_, __) => const SizedBox.shrink(),
|
||||
errorWidget: (_, __, error) {
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
).paddingSymmetrical(isNeedPadding ? 24.w : 0, 0);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue