Compare commits

..

No commits in common. 'master' and 'Courses-CR#-7150' have entirely different histories.

@ -19,11 +19,6 @@ analyzer:
unnecessary_null_checks: error unnecessary_null_checks: error
unnecessary_brace_in_string_interps: error unnecessary_brace_in_string_interps: error
unnecessary_final: error unnecessary_final: error
exclude:
- build/**
- android/**
- ios/**
- web/**
linter: linter:

@ -18,7 +18,7 @@ if (keystorePropertiesFile.exists()) {
android { android {
namespace 'hmg.cloudSolutions.mohem' namespace 'hmg.cloudSolutions.mohem'
compileSdk 37 compileSdk 36
ndkVersion '28.2.13676358' ndkVersion '28.2.13676358'
@ -34,7 +34,7 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "hmg.cloudSolutions.mohem" applicationId "hmg.cloudSolutions.mohem"
minSdkVersion 30 minSdkVersion 30
targetSdk 37 targetSdk 35
targetSdk = flutter.targetSdkVersion targetSdk = flutter.targetSdkVersion
versionCode flutter.versionCode versionCode flutter.versionCode
versionName flutter.versionName versionName flutter.versionName
@ -60,14 +60,12 @@ android {
buildTypes { buildTypes {
debug { debug {
signingConfig signingConfigs.debug signingConfig signingConfigs.debug
minifyEnabled false
shrinkResources false
} }
release { release {
signingConfig signingConfigs.release signingConfig signingConfigs.release
minifyEnabled true minifyEnabled true
shrinkResources true shrinkResources true
proguardFiles 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
} }

@ -4,7 +4,6 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" /> <uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
@ -31,19 +30,11 @@
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<queries>
<intent>
<action android:name="android.speech.RecognitionService" />
</intent>
</queries>
<application <application
android:allowBackup="false" android:allowBackup="false"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="MoheM" android:label="MoheM"
android:extractNativeLibs="true"
android:networkSecurityConfig="@xml/network_security_config" android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"> android:roundIcon="@mipmap/ic_launcher_round">
<provider <provider

@ -4,5 +4,5 @@
<external-files-path name="external-files-path" path="."/> <external-files-path name="external-files-path" path="."/>
<files-path name="files_path" path="."/> <files-path name="files_path" path="."/>
<cache-path name="cache-path" path="."/> <cache-path name="cache-path" path="."/>
<!-- <root-path name="root" path="."/>--> <root-path name="root" path="."/>
</paths> </paths>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<network-security-config> <network-security-config>
<base-config cleartextTrafficPermitted="false"> <base-config cleartextTrafficPermitted="true">
<trust-anchors> <trust-anchors>
<certificates src="system" /> <certificates src="system" />
</trust-anchors> </trust-anchors>

@ -66,34 +66,11 @@ allprojects {
jvmTarget = "21" jvmTarget = "21"
} }
} }
if (project.name == 'nfc_manager') {
project.tasks.matching { it.name.startsWith("compile") && it.name.endsWith("Kotlin") }.configureEach {
doFirst {
def translatorFile = project.file("src/main/kotlin/io/flutter/plugins/nfcmanager/Translator.kt")
if (translatorFile.exists()) {
def content = translatorFile.text
if (content.contains("toLowerCase(Locale.ROOT)")) {
translatorFile.text = content.replace("toLowerCase(Locale.ROOT)", "lowercase(Locale.ROOT)")
}
}
}
}
}
} }
} }
} }
subprojects {
afterEvaluate { project ->
if (project.name == "huawei_push") {
project.android {
compileSdk 36
}
}
}
}
rootProject.buildDir = '../build' rootProject.buildDir = '../build'
subprojects { subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}" project.buildDir = "${rootProject.buildDir}/${project.name}"

@ -1,12 +1,7 @@
#org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m -XX:MaxMetaspaceSize=1024m org.gradle.jvmargs=-Xmx4096m
org.gradle.jvmargs=-XX:MaxMetaspaceSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Xmx4096m -Dfile.encoding=UTF-8
kotlin.daemon.jvmargs=-Xmx4096m kotlin.daemon.jvmargs=-Xmx4096m
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
apmsInstrumentationEnabled=false apmsInstrumentationEnabled=false
#org.gradle.java.home=/Users/amirs/Library/Java/JavaVirtualMachines/jbr-17.0.12/Contents/Home/ #org.gradle.java.home=/Users/amirs/Library/Java/JavaVirtualMachines/jbr-17.0.12/Contents/Home/
# This builtInKotlin flag was added automatically by Flutter migrator
android.builtInKotlin=false
# This newDsl flag was added automatically by Flutter migrator
android.newDsl=false

@ -1,6 +1,6 @@
#Tue Jul 08 11:08:34 AST 2025 #Tue Jul 08 11:08:34 AST 2025
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

@ -30,10 +30,8 @@ pluginManagement {
plugins { plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
// id "com.android.application" version '8.11.0' apply false id "com.android.application" version '8.11.0' apply false
// id("org.jetbrains.kotlin.android") version "2.2.0" apply false id("org.jetbrains.kotlin.android") version "2.2.0" apply false
id "com.android.application" version '9.0.1' apply false
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
id("com.google.gms.google-services") version "4.4.3" apply false id("com.google.gms.google-services") version "4.4.3" apply false
id("com.google.firebase.crashlytics") version "3.0.4" apply false id("com.google.firebase.crashlytics") version "3.0.4" apply false
id('org.gradle.toolchains.foojay-resolver-convention') version '0.9.0' apply false id('org.gradle.toolchains.foojay-resolver-convention') version '0.9.0' apply false

@ -1,46 +0,0 @@
<svg width="74" height="74" viewBox="0 0 74 74" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_119_438)">
<rect x="17" y="17" width="40" height="40" rx="20" fill="url(#paint0_linear_119_438)"/>
</g>
<g filter="url(#filter1_d_119_438)">
<rect x="13" y="13" width="48" height="48" rx="24" fill="url(#paint1_linear_119_438)" fill-opacity="0.4" shape-rendering="crispEdges"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M41.9992 37.5291C40.7637 38.0552 39.4301 38.3796 38.0352 38.4637C37.7946 38.4782 37.5523 38.4856 37.3082 38.4856C37.0642 38.4856 36.8219 38.4782 36.5813 38.4637C35.1861 38.3796 33.8522 38.055 32.6165 37.5287C30.0232 36.4243 27.8623 34.4316 26.4749 31.9094C28.6373 27.9782 32.6791 25.3333 37.3082 25.3333C41.9374 25.3333 45.9792 27.9782 48.1416 31.9094C46.7541 34.4319 44.5928 36.4247 41.9992 37.5291Z" fill="white"/>
<path d="M32.6163 38.5525C32.3175 38.4253 32.0245 38.2863 31.7378 38.136C30.8058 38.8264 30.018 39.6862 29.4248 40.6673C30.4331 42.3347 32.0037 43.6522 33.8885 44.3824C34.7866 44.7303 35.7561 44.9449 36.7701 45.0005H37.8268C38.8406 44.9449 40.6224 46.0011 38.035 48.6666C39.9201 47.9364 44.1637 42.3349 45.1721 40.6673C44.5806 39.689 43.7956 38.8313 42.8671 38.1419C42.5837 38.29 42.2941 38.4272 41.999 38.5529C40.7635 39.079 39.4299 39.4034 38.035 39.4875C37.7945 39.502 37.5521 39.5093 37.3081 39.5093C37.0641 39.5093 36.8217 39.502 36.5811 39.4875C35.1859 39.4034 33.852 39.0788 32.6163 38.5525Z" fill="white"/>
<rect x="32.0844" y="30.2271" width="10.4705" height="3.93258" rx="1.96629" fill="#162550"/>
<ellipse cx="40.2071" cy="32.1717" rx="0.685571" ry="0.720974" fill="#04FED1"/>
<ellipse cx="37.2984" cy="41.8719" rx="0.685571" ry="0.720974" fill="#162550"/>
<ellipse cx="34.5566" cy="32.1717" rx="0.685571" ry="0.720974" fill="#04FED1"/>
<ellipse cx="34.5566" cy="41.8719" rx="0.685571" ry="0.720974" fill="#162550"/>
<ellipse cx="40.0411" cy="41.8719" rx="0.685571" ry="0.720974" fill="#162550"/>
<defs>
<filter id="filter0_d_119_438" x="4" y="4" width="66" height="66" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="6.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0784314 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_119_438"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_119_438" result="shape"/>
</filter>
<filter id="filter1_d_119_438" x="0" y="0" width="74" height="74" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="6.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0784314 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_119_438"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_119_438" result="shape"/>
</filter>
<linearGradient id="paint0_linear_119_438" x1="-12.3178" y1="86.2302" x2="-12.3178" y2="18.3971" gradientUnits="userSpaceOnUse">
<stop offset="1" stop-color="#259CB8"/>
<stop stop-color="#32D892"/>
</linearGradient>
<linearGradient id="paint1_linear_119_438" x1="-22.1814" y1="96.0762" x2="-22.1814" y2="14.6765" gradientUnits="userSpaceOnUse">
<stop offset="1" stop-color="#259CB8"/>
<stop stop-color="#32D892"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

@ -1,4 +0,0 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.3315 5.33146C10.5145 5.1484 10.5145 4.8516 10.3315 4.66854C10.1484 4.48549 9.8516 4.48549 9.66854 4.66854L7.50009 6.83699L5.95644 5.29352C5.77337 5.11048 5.47657 5.11049 5.29352 5.29356C5.11048 5.47663 5.11049 5.77343 5.29356 5.95648L6.83718 7.49991L6.54354 7.79354C6.36049 7.9766 6.36049 8.2734 6.54354 8.45646C6.7266 8.63951 7.0234 8.63951 7.20646 8.45646L7.50013 8.16278L7.79364 8.45625C7.97671 8.6393 8.27351 8.63928 8.45655 8.45621C8.6396 8.27314 8.63958 7.97634 8.45651 7.79329L8.16305 7.49987L10.3315 5.33146Z" fill="#047857"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.53573 1.09375H7.46427C6.09508 1.09374 5.01965 1.09374 4.18001 1.20662C3.3196 1.3223 2.63694 1.5642 2.10057 2.10057C1.5642 2.63694 1.3223 3.3196 1.20662 4.18001C1.09374 5.01965 1.09374 6.09508 1.09375 7.46427V7.53573C1.09374 8.90492 1.09374 9.98035 1.20662 10.82C1.3223 11.6804 1.5642 12.3631 2.10057 12.8994C2.63694 13.4358 3.3196 13.6777 4.18001 13.7934C5.01965 13.9063 6.09507 13.9063 7.46425 13.9062H7.53573C8.90491 13.9063 9.98035 13.9063 10.82 13.7934C11.6804 13.6777 12.3631 13.4358 12.8994 12.8994C13.4358 12.3631 13.6777 11.6804 13.7934 10.82C13.9063 9.98035 13.9063 8.90493 13.9062 7.53575V7.46427C13.9063 6.09509 13.9063 5.01965 13.7934 4.18001C13.6777 3.3196 13.4358 2.63694 12.8994 2.10057C12.3631 1.5642 11.6804 1.3223 10.82 1.20662C9.98035 1.09374 8.90492 1.09374 7.53573 1.09375ZM2.76348 2.76348C3.09664 2.43033 3.54848 2.23746 4.30493 2.13576C5.07487 2.03225 6.08727 2.03125 7.5 2.03125C8.91273 2.03125 9.92513 2.03225 10.6951 2.13576C11.4515 2.23746 11.9034 2.43033 12.2365 2.76348C12.5697 3.09664 12.7625 3.54848 12.8642 4.30493C12.9678 5.07487 12.9688 6.08727 12.9688 7.5C12.9688 8.91273 12.9678 9.92513 12.8642 10.6951C12.7625 11.4515 12.5697 11.9034 12.2365 12.2365C11.9034 12.5697 11.4515 12.7625 10.6951 12.8642C9.92513 12.9678 8.91273 12.9688 7.5 12.9688C6.08727 12.9688 5.07487 12.9678 4.30493 12.8642C3.54848 12.7625 3.09664 12.5697 2.76348 12.2365C2.43033 11.9034 2.23746 11.4515 2.13576 10.6951C2.03225 9.92513 2.03125 8.91273 2.03125 7.5C2.03125 6.08727 2.03225 5.07487 2.13576 4.30493C2.23746 3.54848 2.43033 3.09664 2.76348 2.76348Z" fill="#047857"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

@ -1,3 +0,0 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.8331 11.6668C5.83305 11.7752 5.86321 11.8815 5.9202 11.9737C5.97718 12.066 6.05874 12.1405 6.15572 12.1889L7.3225 12.7723C7.41146 12.8167 7.51031 12.8377 7.60967 12.8332C7.70902 12.8287 7.80557 12.7989 7.89015 12.7466C7.97473 12.6943 8.04454 12.6212 8.09293 12.5343C8.14133 12.4474 8.16671 12.3496 8.16666 12.2502V8.16678C8.16679 7.87766 8.27427 7.5989 8.46827 7.38452L12.6821 2.72418C12.7576 2.64051 12.8073 2.53675 12.8251 2.42544C12.8429 2.31414 12.828 2.20007 12.7824 2.09702C12.7367 1.99396 12.6622 1.90635 12.5678 1.84477C12.4733 1.78318 12.3631 1.75027 12.2504 1.75H1.74938C1.63655 1.75004 1.52616 1.78279 1.43158 1.84429C1.33699 1.90579 1.26226 1.99339 1.21645 2.09648C1.17064 2.19958 1.15571 2.31375 1.17347 2.42516C1.19123 2.53656 1.24091 2.64043 1.3165 2.72418L5.53149 7.38452C5.72549 7.5989 5.83297 7.87766 5.8331 8.16678V11.6668Z" stroke-linecap="round" stroke="#202B33"/>
</svg>

Before

Width:  |  Height:  |  Size: 1001 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 B

@ -1,6 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11 6C11 7.65685 9.65685 9 8 9C6.34315 9 5 7.65685 5 6C5 4.34315 6.34315 3 8 3C9.65685 3 11 4.34315 11 6Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke="#26343D"/>
<path d="M2 20V16C1.9946 14.8394 2.32594 13.7021 2.95386 12.726C3.58179 11.7499 4.47934 10.9768 5.53775 10.5006C6.59616 10.0243 7.76999 9.86518 8.917 10.0426C10.064 10.22 11.1349 10.7262 12 11.5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke="#26343D"/>
<path d="M19.5 11C19.5 12.3807 18.3807 13.5 17 13.5C15.6193 13.5 14.5 12.3807 14.5 11C14.5 9.61929 15.6193 8.5 17 8.5C18.3807 8.5 19.5 9.61929 19.5 11Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke="#26343D"/>
<path d="M12 21V18.5C12 17.1739 12.5268 15.9021 13.4645 14.9645C14.4021 14.0268 15.6739 13.5 17 13.5C18.3261 13.5 19.5979 14.0268 20.5355 14.9645C21.4732 15.9021 22 17.1739 22 18.5V21H12Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke="#26343D"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

@ -1,5 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 9.1875C7.656 9.1875 6.5625 8.094 6.5625 6.75C6.5625 5.406 7.656 4.3125 9 4.3125C10.344 4.3125 11.4375 5.406 11.4375 6.75C11.4375 8.094 10.344 9.1875 9 9.1875ZM9 5.4375C8.27625 5.4375 7.6875 6.02625 7.6875 6.75C7.6875 7.47375 8.27625 8.0625 9 8.0625C9.72375 8.0625 10.3125 7.47375 10.3125 6.75C10.3125 6.02625 9.72375 5.4375 9 5.4375Z" fill="#737373"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.66722 13.5263C8.02722 13.872 8.50047 14.0625 8.99997 14.0625C9.49947 14.0625 9.97272 13.872 10.3327 13.5255C10.5082 13.3568 10.6882 13.1858 10.8705 13.0133C13.1174 10.8818 15.9149 8.22811 14.3692 4.51199C13.4805 2.37374 11.322 0.9375 8.99997 0.9375C6.67797 0.9375 4.52021 2.37374 3.63146 4.51199C2.09096 8.21474 4.86521 10.8555 7.09421 12.9773L7.11578 12.9978C7.30192 13.1753 7.48597 13.3508 7.66722 13.5263ZM4.67022 4.94399C5.38647 3.22049 7.12647 2.0625 8.99997 2.0625C10.8735 2.0625 12.6142 3.22049 13.3312 4.94399C14.5792 7.94549 12.3007 10.107 10.0965 12.1972C10.0547 12.237 10.0129 12.2766 9.97134 12.3162C9.83004 12.4504 9.69004 12.5834 9.55273 12.7155C9.40348 12.8588 9.20697 12.9375 8.99997 12.9375C8.79297 12.9375 8.59646 12.858 8.44871 12.7163C8.25971 12.5333 8.06623 12.3487 7.86973 12.162C5.68581 10.0831 3.42677 7.93263 4.67022 4.94399Z" fill="#737373"/>
<path d="M11.0385 17.0625H6.96142C4.53817 17.0625 3.32169 17.0625 2.72319 16.1745C2.66544 16.0882 2.61517 15.999 2.57392 15.909C2.11666 14.93 2.85295 13.8806 3.87139 12.429L3.87295 12.4267C4.05145 12.1725 4.40243 12.111 4.65668 12.2895C4.91093 12.468 4.97243 12.819 4.79393 13.0733C3.98468 14.2268 3.42443 15.072 3.59543 15.4373C3.61343 15.4763 3.63367 15.5115 3.65692 15.5468C3.92017 15.9375 5.12991 15.9375 6.96138 15.9375H11.0385C12.8699 15.9375 14.0797 15.9375 14.3437 15.546C14.3669 15.5115 14.3872 15.4762 14.4029 15.441C14.5754 15.072 14.0152 14.2268 13.2059 13.0733C13.0274 12.819 13.0889 12.468 13.3432 12.2895C13.5974 12.111 13.9484 12.1725 14.1269 12.4267C15.1462 13.8795 15.8827 14.9295 15.4244 15.9127C15.3847 15.999 15.3352 16.0882 15.2775 16.1737C14.6782 17.0625 13.4617 17.0625 11.0385 17.0625Z" fill="#737373"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

@ -1,5 +0,0 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="arrow-right">
<path id="Vector" d="M3.1245 7.5H11.8755M7.5 11.8755L11.8755 7.5L7.5 3.1245" stroke-width="1.7" stroke-linecap="round" stroke="#147D6B"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 337 B

@ -1,5 +0,0 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="arrow-up-right">
<path id="Vector" d="M14.166 14.166V5.834H5.834M14.166 5.834L5.834 14.166" stroke-width="1.7" stroke-linecap="round" stroke="#143F36"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 338 B

@ -1,5 +0,0 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="chevron-down">
<path id="Vector" d="M3 4.5L6 7.5L9 4.5" stroke-width="1.7" stroke-linecap="round" stroke="#147D6B"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 302 B

@ -1,5 +0,0 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="chevron-left">
<path id="Vector" d="M12.5 15L7.5 10L12.5 5" stroke-width="1.7" stroke-linecap="round" stroke="#202F32"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 306 B

@ -1,10 +0,0 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="compass" clip-path="url(#clip0_0_24)">
<path id="Vector" d="M20.1674 11C20.1674 16.063 16.063 20.1674 11 20.1674C5.93698 20.1674 1.8326 16.063 1.8326 11C1.8326 5.93698 5.93698 1.8326 11 1.8326C16.063 1.8326 20.1674 5.93698 20.1674 11ZM14.8868 7.11281L13.233 12.0733C13.143 12.3433 12.9913 12.5887 12.79 12.79C12.5887 12.9913 12.3433 13.143 12.0733 13.233L7.11281 14.8868L8.76661 9.92628C8.85661 9.65623 9.00827 9.41084 9.20955 9.20955C9.41084 9.00827 9.65623 8.85661 9.92628 8.76661L14.8868 7.11281Z" stroke-width="1.7" stroke-linecap="round" stroke="#147D6B"/>
</g>
<defs>
<clipPath id="clip0_0_24">
<rect width="22" height="22" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 845 B

@ -1,5 +0,0 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="heart">
<path id="Vector" d="M1.2495 5.93763C1.24951 5.24212 1.46051 4.56298 1.85463 3.9899C2.24875 3.41682 2.80744 2.97676 3.45693 2.72785C4.10641 2.47893 4.81612 2.43287 5.49233 2.59575C6.16855 2.75863 6.77944 3.12278 7.24434 3.64011C7.27708 3.67512 7.31667 3.70303 7.36064 3.72211C7.40462 3.74119 7.45204 3.75104 7.49998 3.75104C7.54792 3.75104 7.59534 3.74119 7.63932 3.72211C7.68329 3.70303 7.72288 3.67512 7.75563 3.64011C8.21906 3.11941 8.8301 2.7522 9.50741 2.58735C10.1847 2.42249 10.8962 2.46781 11.5471 2.71728C12.198 2.96674 12.7575 3.40851 13.1511 3.9838C13.5447 4.55909 13.7538 5.2406 13.7505 5.93763C13.7505 7.36889 12.8129 8.43765 11.8753 9.37516L8.44255 12.6958C8.32609 12.8296 8.18249 12.937 8.0213 13.011C7.86011 13.085 7.68502 13.1239 7.50766 13.125C7.3303 13.1261 7.15473 13.0895 6.99262 13.0175C6.83051 12.9456 6.68556 12.84 6.56741 12.7077L3.12464 9.37516C2.18707 8.43765 1.2495 7.37514 1.2495 5.93763Z" stroke-width="1.7" stroke-linecap="round" stroke="#202F32"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

@ -1,5 +0,0 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="heart">
<path id="Vector" d="M1.8326 8.70852C1.83262 7.68845 2.14208 6.69237 2.72012 5.85185C3.29816 5.01134 4.11758 4.36592 5.07016 4.00084C6.02273 3.63577 7.06365 3.56821 8.05542 3.8071C9.0472 4.04598 9.94318 4.58007 10.625 5.33882C10.6731 5.39017 10.7311 5.43111 10.7956 5.45909C10.8601 5.48708 10.9297 5.50152 11 5.50152C11.0703 5.50152 11.1398 5.48708 11.2043 5.45909C11.2688 5.43111 11.3269 5.39017 11.3749 5.33882C12.0546 4.57514 12.9508 4.03656 13.9442 3.79478C14.9376 3.55299 15.9811 3.61946 16.9357 3.98534C17.8904 4.35122 18.711 4.99915 19.2883 5.8429C19.8656 6.68666 20.1722 7.68621 20.1673 8.70852C20.1673 10.8077 18.7922 12.3752 17.4171 13.7502L12.3824 18.6205C12.2116 18.8167 12.001 18.9743 11.7646 19.0828C11.5282 19.1913 11.2714 19.2483 11.0112 19.25C10.7511 19.2516 10.4936 19.1979 10.2558 19.0924C10.0181 18.9869 9.80549 18.8319 9.6322 18.638L4.58281 13.7502C3.20771 12.3752 1.8326 10.8169 1.8326 8.70852Z" stroke-width="1.7" stroke-linecap="round" stroke="#78817F"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 114.3 52">
<defs>
<style>
.cls-1 {
fill: #5e5f5f;
}
.cls-2 {
fill: #fff;
}
.cls-3 {
fill: #ec1f2d;
}
</style>
</defs>
<g id="Layer_1-2" data-name="Layer 1">
<g>
<path fill="#5e5f5f" d="M15.74,25.46H3.99c-2.53,0-3.99-1.42-3.99-3.78v-11.01h3.31v9.33c0,1.56.4,2.39,2.84,2.39h9.59v3.07h0Z"/>
<path fill="#5e5f5f" d="M17.2,29.26h-2.93v-2.66h2.93v2.66ZM21.88,21.03c0,1.21-.35,2.22-1.04,3.03-.78.94-1.87,1.4-3.24,1.4h-2.81v-3.07h2.1c1.11,0,1.68-.76,1.68-2.25v-5.07h3.31v5.96ZM21.67,29.26h-2.95v-2.66h2.95v2.66Z"/>
<path fill="#5e5f5f" d="M27.6,25.46h-3.31v-14.79h3.31v14.79Z"/>
<path fill="#5e5f5f" d="M39.98,25.46h-3.66c-.54,2.52-2.67,3.78-6.43,3.78v-2.59c2.17,0,3.26-1.01,3.26-3.03v-8.55h3.33v7.32h3.5v3.07h0ZM36.27,13.32h-2.93v-2.64h2.93v2.64Z"/>
<path fill="#5e5f5f" d="M53.17,22.2c0,3.07-2.13,5.23-5.44,5.23-2.01,0-3.52-.67-4.49-1.97h-4.25v-3.07h3.28c0-3.28,1.94-5.37,5.34-5.37s5.55,2.04,5.55,5.18ZM49.89,22.31c0-1.81-.71-2.73-2.1-2.73h-.24c-1.21,0-2.03,1.08-2.03,2.59s.8,2.64,2.22,2.64c1.3,0,2.15-1.01,2.15-2.5h0Z"/>
<path fill="#ec1f2d" d="M104.69,0h-34.38c-5.31,0-9.61,4.17-9.61,9.32v33.36c0,5.15,4.3,9.32,9.61,9.32h34.38c5.31,0,9.61-4.17,9.61-9.32V9.32c0-5.15-4.3-9.32-9.61-9.32Z"/>
<path fill="#ffffff" d="M107.74,34.57l-.03,10.61-14.28-.04c-1.28,0-2.16-1.23-2.22-3.11,0-2.95-.9-5.86-2.69-8.25-3.37-4.51-8.12-8.12-14.29-10.8-1.52-.66-3.18-1-4.85-.99-1.32-.08-2.2-1.54-2.2-3.37v-10.57s14.09,0,14.09,0c1.27,0,2.28,1.27,2.34,3.15,0,2.05.58,4.07,1.72,5.79,3.85,5.77,8.76,10.4,15.33,13.26,1.52.66,3.18.99,4.85,1,1.32.08,2.22,1.5,2.22,3.32Z"/>
<path fill="#ffffff" d="M83.66,32.91c-.01.15-.02.3-.02.46v7.34s-.01,1.27-.01,1.27c-.07,1.88-1.12,3.19-2.39,3.19l-10.64-.04c-1.4,0-3.39.01-3.39.01l-.02-10.65c0-1.82.89-3.27,2.21-3.36h3.95s1.51,0,1.51,0h6.83c.92,0,1.7.75,1.98,1.78h0Z"/>
<path fill="#ffffff" d="M107.81,5.37l-.02,13.27c0,1.83-.85,3.33-2.17,3.41h-4s-1.34-.01-1.34-.01c-.09-.02-.19-.02-.28-.02h-6.81c-.9,0-1.67-.72-1.96-1.72.02-.16.03-.33.04-.51v-8.51c.06-1.89,1.12-3.22,2.39-3.22h9.73c2.94-.14,4.43-2.68,4.43-2.68h0Z"/>
<path fill="#5e5f5f" d="M8.84,38.19c-.04-.99-.08-2.18-.08-3.37h-.04c-.26,1.05-.61,2.22-.94,3.18l-1.03,3.2h-1.49l-.9-3.18c-.28-.96-.57-2.13-.77-3.2h-.03c-.05,1.11-.09,2.38-.15,3.4l-.15,3.12h-1.77l.54-8.21h2.55l.83,2.74c.26.95.53,1.97.72,2.94h.04c.24-.95.53-2.03.8-2.95l.9-2.73h2.5l.46,8.21h-1.87l-.12-3.14h0Z"/>
<path fill="#5e5f5f" d="M14.49,39.23l-.6,2.11h-1.98l2.59-8.21h2.51l2.62,8.21h-2.06l-.65-2.11h-2.42ZM16.63,37.84l-.53-1.74c-.15-.49-.3-1.1-.43-1.58h-.03c-.13.49-.25,1.11-.39,1.58l-.5,1.74h1.87Z"/>
<path fill="#5e5f5f" d="M21.45,40.34l4.13-5.63v-.05h-4.13v-1.53h6.67v1.07l-4.04,5.55v.05h4.1v1.53h-6.74v-1h0Z"/>
<path fill="#5e5f5f" d="M32.58,39.23l-.6,2.11h-1.98l2.59-8.21h2.51l2.62,8.21h-2.06l-.65-2.11h-2.42ZM34.73,37.84l-.53-1.74c-.15-.49-.3-1.1-.43-1.58h-.03c-.13.49-.25,1.11-.39,1.58l-.5,1.74h1.87Z"/>
<path fill="#5e5f5f" d="M39.86,41.34v-3.36l-2.67-4.85h2.18l.87,1.97c.26.6.44,1.04.65,1.57h.03c.19-.51.39-.99.64-1.57l.85-1.97h2.15l-2.79,4.79v3.42h-1.91,0Z"/>
<path fill="#5e5f5f" d="M46.54,39.23l-.6,2.11h-1.98l2.59-8.21h2.51l2.62,8.21h-2.06l-.65-2.11h-2.42ZM48.69,37.84l-.53-1.74c-.15-.49-.3-1.1-.43-1.58h-.03c-.13.49-.25,1.11-.39,1.58l-.5,1.74h1.87Z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.02 94.61">
<defs>
<style>
.cls-1 {
fill: #5e5f5f;
}
.cls-2 {
fill: #ec222e;
}
.cls-3 {
fill: #fff;
}
</style>
</defs>
<g id="Layer_1-2" data-name="Layer 1">
<g>
<g>
<path fill="#5e5f5f" d="M16.59,77.8H4.53c-2.6,0-4.1-1.5-4.1-4v-11.65h3.4v9.88c0,1.65.41,2.52,2.91,2.52h9.85v3.25Z"/>
<path fill="#5e5f5f" d="M18.09,81.83h-3.01v-2.81h3.01v2.81ZM22.89,73.12c0,1.29-.36,2.35-1.07,3.2-.8.99-1.92,1.48-3.32,1.48h-2.89v-3.25h2.16c1.14,0,1.72-.8,1.72-2.38v-5.36h3.4v6.31ZM22.67,81.83h-3.03v-2.81h3.03v2.81Z"/>
<path fill="#5e5f5f" d="M28.76,77.8h-3.4v-15.65h3.4v15.65Z"/>
<path fill="#5e5f5f" d="M41.48,77.8h-3.76c-.56,2.67-2.74,4-6.6,4v-2.74c2.23,0,3.35-1.07,3.35-3.2v-9.05h3.42v7.74h3.59v3.25ZM37.67,64.97h-3.01v-2.79h3.01v2.79Z"/>
<path fill="#5e5f5f" d="M55.02,74.36c0,3.25-2.18,5.53-5.58,5.53-2.06,0-3.62-.7-4.61-2.09h-4.37v-3.25h3.37c0-3.47,1.99-5.68,5.48-5.68s5.7,2.16,5.7,5.48ZM51.64,74.48c0-1.92-.73-2.89-2.16-2.89h-.24c-1.24,0-2.09,1.14-2.09,2.74s.82,2.79,2.28,2.79c1.33,0,2.21-1.07,2.21-2.64Z"/>
</g>
<g>
<rect fill="#ffffff" y="0" width="55.02" height="55.02" rx="9.86" ry="9.86"/>
<g>
<path fill="#ec1f2d" d="M48.28,36.58l-.03,11.22-14.66-.04c-1.31,0-2.21-1.3-2.27-3.29h0c0-3.13-.93-6.2-2.76-8.73-3.46-4.77-8.33-8.59-14.67-11.43-1.56-.7-3.26-1.06-4.98-1.05h0c-1.35-.09-2.26-1.63-2.26-3.56v-11.19s14.46,0,14.46,0c1.31,0,2.34,1.34,2.4,3.33h0c0,2.17.59,4.31,1.77,6.13,3.95,6.11,9,11,15.74,14.03,1.56.7,3.26,1.05,4.98,1.06h0c1.35.09,2.28,1.58,2.28,3.52Z"/>
<path fill="#ec1f2d" d="M23.56,34.82c-.01.16-.02.32-.02.48v7.77s-.02,1.34-.02,1.34c-.07,1.99-1.15,3.37-2.45,3.37l-10.92-.05c-1.44,0-3.48.01-3.48.01l-.02-11.27c0-1.93.91-3.46,2.26-3.55h4.05s.26,0,.26,0h1.3s7.01,0,7.01,0c.94,0,1.75.79,2.03,1.88Z"/>
<path fill="#ec1f2d" d="M48.36,5.69l-.02,14.04c0,1.93-.88,3.52-2.23,3.61h-4.1s-1.38-.01-1.38-.01c-.1-.02-.19-.02-.29-.02h-6.99c-.93,0-1.72-.76-2.01-1.82.02-.17.03-.35.04-.54v-9.01c.06-2,1.15-3.41,2.45-3.41h9.99c3.02-.15,4.55-2.83,4.55-2.83Z"/>
</g>
</g>
<g>
<path fill="#5e5f5f" d="M9.5,91.28c-.04-1.04-.08-2.31-.08-3.57h-.04c-.27,1.11-.63,2.35-.97,3.36l-1.06,3.39h-1.53l-.93-3.36c-.28-1.02-.58-2.26-.79-3.39h-.03c-.05,1.17-.09,2.51-.15,3.6l-.15,3.3h-1.82l.55-8.69h2.62l.85,2.9c.27,1.01.54,2.09.73,3.11h.04c.24-1.01.54-2.15.82-3.12l.93-2.89h2.56l.48,8.69h-1.92l-.13-3.32Z"/>
<path fill="#5e5f5f" d="M15.3,92.38l-.62,2.23h-2.04l2.66-8.69h2.58l2.69,8.69h-2.11l-.67-2.23h-2.49ZM17.51,90.91l-.54-1.84c-.15-.52-.31-1.16-.44-1.68h-.03c-.13.52-.26,1.17-.4,1.68l-.52,1.84h1.92Z"/>
<path fill="#5e5f5f" d="M22.45,93.55l4.24-5.95v-.05h-4.24v-1.62h6.85v1.13l-4.15,5.88v.05h4.21v1.62h-6.92v-1.06Z"/>
<path fill="#5e5f5f" d="M33.88,92.38l-.62,2.23h-2.04l2.66-8.69h2.58l2.69,8.69h-2.11l-.67-2.23h-2.49ZM36.08,90.91l-.54-1.84c-.15-.52-.31-1.16-.44-1.68h-.03c-.13.52-.26,1.17-.4,1.68l-.52,1.84h1.92Z"/>
<path fill="#5e5f5f" d="M41.35,94.61v-3.56l-2.75-5.13h2.24l.89,2.09c.27.63.45,1.1.67,1.66h.03c.19-.54.4-1.04.66-1.66l.88-2.09h2.2l-2.86,5.07v3.62h-1.96Z"/>
<path fill="#5e5f5f" d="M48.21,92.38l-.62,2.23h-2.04l2.66-8.69h2.58l2.69,8.69h-2.11l-.67-2.23h-2.49ZM50.41,90.91l-.54-1.84c-.15-.52-.31-1.16-.44-1.68h-.03c-.13.52-.26,1.17-.4,1.68l-.52,1.84h1.92Z"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.5 KiB

@ -1,5 +0,0 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="map-pin">
<path id="Vector" d="M9.9996 4.99997C9.9996 7.4967 7.23038 10.0969 6.30047 10.9C6.21384 10.9652 6.10839 11.0004 6 11.0004C5.89161 11.0004 5.78616 10.9652 5.69953 10.9C4.76962 10.0969 2.0004 7.4967 2.0004 4.99997C2.0004 3.93901 2.42179 2.9215 3.17186 2.17128C3.92193 1.42107 4.93924 0.9996 6 0.9996C7.06076 0.9996 8.07807 1.42107 8.82814 2.17128C9.57822 2.9215 9.9996 3.93901 9.9996 4.99997ZM7.49985 4.99997C7.49985 5.82847 6.82834 6.50011 6 6.50011C5.17166 6.50011 4.50015 5.82847 4.50015 4.99997C4.50015 4.17147 5.17166 3.49983 6 3.49983C6.82834 3.49983 7.49985 4.17147 7.49985 4.99997Z" stroke-linecap="round" stroke="#147D6B"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 826 B

@ -1,5 +0,0 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="search">
<path id="Vector" d="M15.7501 15.7501L12.4951 12.4951M14.25 8.25C14.25 11.5637 11.5637 14.25 8.25 14.25C4.93629 14.25 2.25 11.5637 2.25 8.25C2.25 4.93629 4.93629 2.25 8.25 2.25C11.5637 2.25 14.25 4.93629 14.25 8.25Z" stroke-width="1.7" stroke-linecap="round" stroke="#78817F"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 472 B

@ -1,5 +0,0 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="user-round">
<path id="Vector" d="M11 11.9167C13.5311 11.9167 15.5829 9.86464 15.5829 7.33333C15.5829 4.80203 13.5311 2.75 11 2.75C8.46895 2.75 6.41712 4.80203 6.41712 7.33333C6.41712 9.86464 8.46895 11.9167 11 11.9167ZM11 11.9167C12.9447 11.9167 14.8098 12.6893 16.1849 14.0646C17.5601 15.4398 18.3326 17.3051 18.3326 19.25M11 11.9167C9.05527 11.9167 7.1902 12.6893 5.81507 14.0646C4.43994 15.4398 3.6674 17.3051 3.6674 19.25" stroke-width="1.7" stroke-linecap="round" stroke="#78817F"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 674 B

@ -1,12 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_120_1007)">
<path d="M13.125 5C13.125 3.27411 11.7259 1.875 10 1.875C8.27411 1.875 6.875 3.27411 6.875 5V10C6.875 11.7259 8.27411 13.125 10 13.125C11.7259 13.125 13.125 11.7259 13.125 10V5Z" stroke="#697586" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 15.625V18.75" stroke="#697586" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15.625 10C15.625 11.4918 15.0324 12.9226 13.9775 13.9775C12.9226 15.0324 11.4918 15.625 10 15.625C8.50816 15.625 7.07742 15.0324 6.02252 13.9775C4.96763 12.9226 4.375 11.4918 4.375 10" stroke="#697586" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_120_1007">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 903 B

@ -1,3 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.8744 6.13098L7.58175 10.4694L2.69958 7.41575C2.00006 6.97811 2.14558 5.91558 2.93679 5.6842L14.5284 2.28958C15.253 2.07722 15.9244 2.75462 15.7092 3.4815L12.2798 15.0651C12.0448 15.8574 10.9884 15.999 10.5549 15.2965L7.5795 10.4701" stroke="#0D121C" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 434 B

@ -1,6 +0,0 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.8848 0.782465C11.6672 0.777969 11.077 0.780422 10.3952 0.783256L10.074 0.784571C9.25489 0.787826 8.36176 0.790261 7.8034 0.782363L3.41832 0.782363C3.30775 0.782363 3.09409 0.784794 2.86663 0.826585C2.65209 0.866 2.34887 0.953285 2.12257 1.17828C2.01295 1.28728 1.92531 1.4256 1.85771 1.5488C1.78696 1.67774 1.72248 1.81951 1.66613 1.95532C1.55744 2.2173 1.46792 2.48443 1.41557 2.64068L1.41079 2.65493L0.986568 3.88476C0.919741 4.06317 0.849387 4.33704 0.811103 4.61007C0.791433 4.75035 0.778628 4.90141 0.781624 5.04747C0.784495 5.18744 0.802226 5.35457 0.864472 5.50929C0.963954 5.75657 1.18158 6.01838 1.36007 6.20866C1.36856 6.21771 1.3771 6.22675 1.38569 6.23576C1.38552 6.24088 1.38544 6.24602 1.38544 6.25118L1.38544 9.41047C1.38543 10.2652 1.38541 10.9542 1.45827 11.496C1.5339 12.0586 1.69571 12.5322 2.0719 12.9084C2.4481 13.2846 2.92177 13.4465 3.48434 13.5221C4.0262 13.595 4.71515 13.5949 5.5699 13.5949L7.47919 13.5949C7.73807 13.5949 7.94794 13.3851 7.94794 13.1262C7.94794 12.8673 7.73807 12.6574 7.47919 12.6574L5.60419 12.6574C4.70705 12.6574 4.08136 12.6564 3.60926 12.593C3.15066 12.5313 2.9078 12.4185 2.73482 12.2455C2.56184 12.0726 2.44906 11.8297 2.38741 11.3711C2.32393 10.899 2.32294 10.2733 2.32294 9.37618L2.32294 6.97328C2.45051 7.03849 2.59529 7.10093 2.75359 7.15501C3.17457 7.29882 3.7203 7.39381 4.3406 7.31265C4.89853 7.23967 5.351 7.00851 5.70918 6.71013C6.09419 7.00458 6.70331 7.30587 7.47811 7.31657C8.04917 7.32446 8.56216 7.12979 8.95868 6.90312C9.08184 6.83271 9.19674 6.75753 9.30188 6.68132C9.77763 7.08237 10.6727 7.56818 11.9991 7.20432C13.5774 6.77137 14.083 5.75144 14.209 5.1425C14.2154 5.11127 14.2187 5.07946 14.2187 5.04757C14.2187 4.75732 14.1289 4.33991 13.9659 3.80861C13.7986 3.26346 13.5405 2.55589 13.1738 1.66657C13.1701 1.65753 13.166 1.6486 13.1618 1.63981C13.0956 1.50407 12.9647 1.3046 12.7653 1.13192C12.5616 0.95548 12.2655 0.790329 11.8848 0.782465ZM7.49106 6.37916C6.97962 6.3721 6.58006 6.18008 6.33319 6.00595C6.55391 5.67199 6.6951 5.33553 6.76663 5.07693C6.83564 4.82742 6.68931 4.5692 6.4398 4.50019C6.19028 4.43117 5.93206 4.5775 5.86305 4.82702C5.70947 5.3823 5.16544 6.25926 4.21899 6.38308C3.76402 6.4426 3.36434 6.37295 3.05665 6.26784C2.73842 6.15914 2.54601 6.02235 2.50231 5.98113C2.48528 5.96506 2.46707 5.9503 2.44784 5.93696C2.37021 5.88311 2.20722 5.74146 2.04383 5.56728C1.87113 5.38316 1.76292 5.23071 1.73421 5.15935C1.73421 5.15935 1.73024 5.14811 1.72656 5.12505C1.72269 5.10085 1.71976 5.0687 1.71893 5.02824C1.71725 4.94644 1.72447 4.84757 1.73952 4.74025C1.77015 4.52178 1.82659 4.31346 1.86579 4.21019C1.86748 4.20572 1.86911 4.20123 1.87067 4.19671L2.29773 2.95872L2.30261 2.94414C2.3555 2.78638 2.43617 2.5457 2.53206 2.3146C2.58103 2.19657 2.63099 2.0884 2.67962 1.99977C2.73141 1.90538 2.7681 1.85849 2.78357 1.8431C2.80747 1.81934 2.88262 1.77684 3.03603 1.74865C3.17652 1.72284 3.32038 1.71986 3.41832 1.71986L7.79347 1.71986C8.36231 1.72785 9.26287 1.7253 10.0778 1.72206L10.3974 1.72076C11.0847 1.71792 11.6606 1.71553 11.8655 1.71977C11.9719 1.72196 12.0657 1.76626 12.1515 1.84057C12.2347 1.91263 12.2907 1.99706 12.314 2.0407C12.6704 2.90597 12.9148 3.57905 13.0696 4.08359C13.214 4.55415 13.2701 4.84632 13.2797 5.00173C13.1969 5.33137 12.8878 5.98842 11.7511 6.30022C10.8759 6.54033 10.2955 6.26948 9.96995 6.01784C10.0887 5.82356 10.2297 5.52285 10.3667 5.09485C10.4456 4.84828 10.3097 4.58443 10.0631 4.50553C9.81653 4.42662 9.55268 4.56254 9.47378 4.8091C9.30754 5.32855 9.16081 5.54992 9.1304 5.58829C9.02102 5.71208 8.79354 5.91764 8.4934 6.08922C8.18993 6.2627 7.84298 6.38402 7.49106 6.37916Z" fill="#C2410C"/>
<path d="M3.88544 10.6262C3.88544 10.3673 4.0953 10.1574 4.35419 10.1574L6.85419 10.1574C7.11307 10.1574 7.32294 10.3673 7.32294 10.6262C7.32294 10.8851 7.11307 11.0949 6.85419 11.0949L4.35419 11.0949C4.0953 11.0949 3.88544 10.8851 3.88544 10.6262Z" fill="#C2410C"/>
<path d="M10.9167 10.6262C10.9167 10.281 11.1965 10.0012 11.5417 10.0012L11.5473 10.0012C11.8925 10.0012 12.1723 10.281 12.1723 10.6262C12.1723 10.9714 11.8925 11.2512 11.5473 11.2512L11.5417 11.2512C11.1965 11.2512 10.9167 10.9714 10.9167 10.6262Z" fill="#C2410C"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5624 7.96748C10.1037 7.96748 8.90619 9.13055 8.90619 10.5835C8.90619 11.4113 9.32257 12.0297 9.75433 12.5247C9.9082 12.7011 10.0773 12.876 10.2353 13.0394C10.2951 13.1013 10.3536 13.1618 10.4088 13.2199C10.619 13.4408 10.7995 13.6441 10.9391 13.8516C11.0558 14.0251 11.2645 14.2195 11.5661 14.2195C11.8728 14.2195 12.0808 14.0193 12.1939 13.8391C12.3264 13.6278 12.5016 13.425 12.7082 13.2072C12.7757 13.1361 12.85 13.0599 12.9271 12.981C13.0706 12.8339 13.2237 12.677 13.3595 12.525C13.7946 12.038 14.2187 11.4281 14.2187 10.5835C14.2187 9.13055 13.0212 7.96748 11.5624 7.96748ZM9.84369 10.5835C9.84369 9.66465 10.605 8.90498 11.5624 8.90498C12.5199 8.90498 13.2812 9.66465 13.2812 10.5835C13.2812 11.0803 13.0441 11.4709 12.6604 11.9004C12.5358 12.0398 12.4117 12.1667 12.28 12.3015L12.2796 12.302C12.1992 12.3842 12.1159 12.4694 12.028 12.562C11.869 12.7297 11.7058 12.9111 11.5583 13.1097C11.4095 12.9183 11.2464 12.7402 11.0881 12.5738C11.0212 12.5034 10.956 12.436 10.8923 12.3701C10.7408 12.2134 10.5979 12.0656 10.4608 11.9084C10.0786 11.4703 9.84369 11.0692 9.84369 10.5835Z" fill="#C2410C"/>
</svg>

Before

Width:  |  Height:  |  Size: 5.3 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 730 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 37 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 794 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 29 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 37 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

File diff suppressed because it is too large Load Diff

@ -315,8 +315,6 @@
"requestType": "Request Type", "requestType": "Request Type",
"requestCreatedSuccessfully": "Request created successfully", "requestCreatedSuccessfully": "Request created successfully",
"search": "Search", "search": "Search",
"filterEnterPrDetailsHere": "Enter PR Details here",
"filterEnterPoDetailsHere": "Enter PO Details here",
"wantToReject": "Are you sure want to reject?", "wantToReject": "Are you sure want to reject?",
"employeeDigitalID": "Employee Digital ID", "employeeDigitalID": "Employee Digital ID",
"businessCard": "Business Card", "businessCard": "Business Card",
@ -549,15 +547,6 @@
"manage": "Manage", "manage": "Manage",
"members": "Members", "members": "Members",
"areYouSureWantTodelete": "Are you sure want to delete?", "areYouSureWantTodelete": "Are you sure want to delete?",
"confirmGrantMazayaAccess": "Are you sure you want to grant Mazaya access to \"{}\"?",
"confirmRevokeMazayaAccess": "Are you sure you want to revoke Mazaya access to \"{}\"?",
"yes": "Yes",
"no": "No",
"mazayaInviteInstructions": "Please download the MoheM app from the above link and use the following credentials to login",
"cameraPermissionRequiredToScan": "Please grant camera permission to scan the QR code",
"cameraPermissionPermanentlyDenied": "Please allow camera permission from the app settings > Permissions",
"openSettings": "Open Settings",
"nationalIdOrIqamaFallback": "Your National ID or Iqama Number",
"groupMembers": "Group Members", "groupMembers": "Group Members",
"manageGroup": "Manage Group", "manageGroup": "Manage Group",
"admin": "Admin", "admin": "Admin",
@ -579,6 +568,7 @@
"pleaseClickButtonToJoinMarathon": "Press the button below to join the Marathon.", "pleaseClickButtonToJoinMarathon": "Press the button below to join the Marathon.",
"youCannotJoinTheMarathon": "You cannot join the Marathon because you have exceeded the time limit.", "youCannotJoinTheMarathon": "You cannot join the Marathon because you have exceeded the time limit.",
"requesterOperatingUnit": "Requester Operating Unit", "requesterOperatingUnit": "Requester Operating Unit",
"generate": "Generate",
"paymentRequest": "Pay Request", "paymentRequest": "Pay Request",
"paymentDetails": "Payment Details", "paymentDetails": "Payment Details",
"requestNo": "Request No", "requestNo": "Request No",
@ -676,220 +666,7 @@
"ticketNumber": "Ticket Number", "ticketNumber": "Ticket Number",
"ticketId": "Ticket ID", "ticketId": "Ticket ID",
"pleaseSelectProject": "Please select a project", "pleaseSelectProject": "Please select a project",
"viewAttachment": "View Attachment", "viewAttachment" : "View Attachment",
"parkingQr": "Parking QR", "parkingQr": "Parking QR",
"startNow": "Start Now", "startNow": "Start Now"
"destination_type": "Destination Type",
"source_organization_name": "Source Organization Name",
"destination_organization_name": "Destination Organization Name",
"costCenter": "Cost Center",
"priceSAR": "Price (SAR)",
"amountSAR": "Amount (SAR)",
"amuLastThreeMonths": "AMU (Last 3 months)",
"mazayaTitle": "Mazaya",
"mazayaAccessGranted": "Mazaya access granted",
"mazayaAccessGrantedDesc": "{} now has active access. Share the download link below so they can install the Mohem app and complete setup.",
"accessIsNowActive": "Access is now active",
"accessIsNowActiveDesc": "{} can use the provided link to download the Mazaya app and continue setup on their device.",
"dependentAccessActive": "Dependent access active",
"shareableAppDownloadLink": "Shareable app-download link",
"shareableAppDownloadLinkDesc": "Send this link to {} so they can download the Mazaya app and continue setup.",
"downloadLink": "Download link",
"shareDownloadLink": "Share Download Link",
"copyLink": "Copy Link",
"done": "Done",
"linkCopied": "Link copied to clipboard",
"mazayaAccessRevoked": "Mazaya Access",
"mazayaAccessRevokedDesc": "You are not authorized to access the Mazaya Program.",
"contactYourSponsor": "Contact your sponsor",
"contactYourSponsorDesc": "Ask your sponsor to allow Mazaya access again from Dependent Details. Only the sponsor can restore access.",
"dependentAccessBlocked": "Dependent access blocked",
"returnToSignIn": "Return to Sign In",
"allowMazayaAccess": "Allow Mazaya Access",
"revokeMazayaAccess": "Revoke Mazaya Access",
"active": "Active",
"noAccess": "No access",
"offerDetails": "Offer Details",
"offerIdNotAvailable": "Offer ID not available",
"unableToRedeemOffer": "Unable to redeem offer, please try again later",
"errorRedeemingOffer": "An error occurred while redeeming offer",
"useOffer": "Use Offer",
"online": "Online",
"inStore": "In Store",
"ksa": "KSA",
"riyadh": "Riyadh",
"noInstructionsAvailable": "No instructions available.",
"contactInfo": "Contact Information",
"allCategories": "All",
"noOffersAvailable": "No offers available",
"addDependents": "Add Dependents",
"myDependents": "My Dependents",
"offDiscount": "{} Off",
"usageLabel": "Usage: ",
"areYouSureWantToLogOut": "Are you sure you want to log out?",
"searchDependents": "Search dependents",
"searchFamilyMembers": "Search family members",
"addAnotherUser": "Add another user",
"joinMazaya": "Join Mazaya - Coming Soon",
"joinMazayaSubtitle": "Get the best offers and exclusive deals across dining, shopping, and lifestyle rewards.",
"continueAsEmployee": "Continue as an employee",
"continueAsEmployeeSubtitle": "Sign in with your employee username and password.",
"pleaseEnterNationalId": "Please enter National ID",
"pleaseSelectDob": "Please select Date of Birth",
"unableToRetrieveDependentInfo": "Unable to retrieve dependent information",
"failedToSendOtp": "Failed to send verification code",
"verificationCodeResent": "Verification code resent",
"nationalId": "National ID",
"addDependent": "Add Dependent",
"stepVerify": "Verify",
"stepBasic": "Basic",
"stepAdditional": "Additional",
"findYourDependent": "Find your dependent",
"findYourDependentDesc": "Enter the details exactly as they appear on the National ID. We'll securely retrieve the record from NHIC.",
"enter10DigitNationalId": "Enter 10-digit National ID",
"nationalIdMustBe10Digits": "National ID must be 10 digits",
"dateFormatPlaceholder": "DD / MM / YYYY",
"searchUser": "Search User",
"dependentNotFoundNhic": "Sorry, error while getting dependent info from NHIC",
"basicInfo": "Basic Info",
"basicInfoDesc": "Required identity details fetched from NHIC.",
"additionalInfo": "Additional Info",
"additionalInfoDesc": "Add or review sponsorship and eligibility details.",
"select": "Select",
"enterFieldPlaceholder": "Enter {}",
"requiredField": "Required field",
"pleaseFillField": "Please fill {}",
"reviewAndSubmit": "Review & Submit",
"reviewAndSubmitDesc": "Please review the dependent details carefully before submitting.",
"arabicName": "Arabic Name",
"relationship": "Relationship",
"sponsor": "Sponsor",
"dependentSponsorDetail": "Dependent Sponsor Detail",
"insuranceEligibility": "Insurance Eligibility",
"relationshipStartDate": "Start Date",
"dependentAddedSuccessfully": "Dependent added successfully",
"genderMale": "Male",
"genderFemale": "Female",
"religionIslam": "Islam",
"back": "Back",
"submitRequest": "Submit",
"enterDependentDetailsToContinue": "Enter the dependent details to continue.",
"enterBasicDetailsDesc": "Please enter the dependent's basic identity details.",
"locations": "Locations",
"availableOffers": "Available Offers",
"selectOfferToRedeem": "Select an offer to use",
"viewOnMap": "View on map",
"showAllLocations": "Show all {} locations",
"searchBranches": "Search branches or cities...",
"allCities": "All",
"allCitiesOption": "All Cities",
"noBranchesFound": "No branches found",
"browseAllBranches": "Browse all {} branches across cities",
"branchesCount": "{} Branches",
"kmAway": "{} KM Away",
"dependentDetails": "Dependent Details",
"keyDetails": "Key details",
"verified": "Verified",
"idEnding": "ID ending {}",
"servicesAndActions": "Services & actions",
"startDependentFlowsDesc": "Start dependent flows and manage access from one place.",
"mazayaAccess": "Mazaya access",
"accessOff": "Off",
"allowDependentMazayaOffersDesc": "Allow {} to use Mazaya offers with her verified identity.",
"loginMethod": "Login method",
"nationalIdAndDob": "National ID & Date of Birth",
"sponsorOtp": "Sponsor OTP",
"revokingPreventsAccessDesc": "Revoking prevents all future dependent access until you allow it again.",
"locationPermissionRequiredForAttendance": "You need to give location permission to mark attendance",
"locationServicesRequiredForAttendance": "You need to enable location services to mark attendance",
"pleaseProvideLocationPermission": "Please provide location permission",
"microphoneAccessRequired": "The app needs microphone access to be able to record audio.",
"shareAccessGuide": "Share Access Guide",
"nationalIdOrIqamaNumber": "National ID or Iqama Number",
"multipleCities": "Multi-city",
"filters": "Filters",
"availability": "Availability",
"chooseOneOrBoth": "Choose one or both",
"sortBy": "Sort by",
"recommended": "Recommended",
"nearest": "Nearest",
"highestDiscount": "Highest discount",
"newest": "Newest",
"distance": "Distance",
"fromYourCurrentLocation": "From your current location",
"any": "Any",
"minimumDiscount": "Minimum discount",
"reset": "Reset",
"applyFilters": "Apply filters",
"distanceKm": "{} km",
"discountPercentPlus": "{}%+",
"visitMerchantWebsite": "Visit Merchant Website",
"merchantWebsiteNotAvailable": "Merchant website not available",
"locationServicesRequiredForNearestOffers": "You need to enable location services to show nearest offers",
"locationPermissionRequiredForNearestOffers": "You need to give location permission to show nearest offers",
"mazayaDeals": "Mazaya Deals",
"helloName": "Hello, {}",
"findABrandOrSomething": "Find a brand or a little something",
"exploreYourBenefits": "Explore your benefits",
"theMazayaEdit": "The Mazaya Edit",
"exclusivelyYours": "Exclusively HMG",
"aLittleExtraJustForMazayaMembers": "A little extra, just for Mazaya members.",
"seeAll": "See all",
"latestOnMazaya": "Latest on Mazaya",
"goodFoodGreatCompanyEvenBetterPerks": "Good food. Great company. Even better perks.",
"travelEssentials": "Travel Essentials",
"carServices": "Car Services",
"fromEverydayCareToYourNextAdventure": "From everyday care to your next adventure.",
"homeAndKitchen": "Home & Kitchen",
"curatedPicksForYourHomeAndKitchen": "Curated picks for your home and kitchen.",
"discover": "Discover",
"saved": "Saved",
"myFamily": "My Family",
"educationAndTraining": "Education & Training",
"foodsAndDrinks": "Foods & Drinks",
"travelAndEntertainment": "Travel & Entertainment",
"beautyAndFitness": "Beauty & Fitness",
"healthcare": "Healthcare",
"favoriteAddedSuccess": "Added to favorites successfully",
"favoriteRemovedSuccess": "Removed from favorites successfully",
"favoriteError": "An error occurred while updating favorites",
"favoriteMerchant": "Favorite Offers",
"favoriteOffers": "Favorite Offers",
"goodToKnow": "Good to know",
"goodToKnowDesc": "Keep your Mazaya membership handy. Check the merchant's availability, eligible activities and full terms before you book.",
"termsAndConditions": "Terms & conditions",
"haveAQuestionAboutOffer": "Have a question about this offer?",
"getHelp": "Get help",
"redeemOffer": "Redeem offer",
"merchantContact": "Merchant contact",
"website": "Website",
"phone": "Phone",
"viewAll": "View all",
"moreCitiesCount": "+ {} more cities",
"mazayaDeals": "Mazaya Deals",
"exploreYourBenefits": "Explore your benefits",
"findAnEscapeOrAnExperience": "Find an escape or an experience",
"allOffers": "All offers",
"sampleAvailability": "sample availability",
"offersCount": "{} offers",
"mazayaExclusive": "MAZAYA EXCLUSIVE",
"youAreAllCaughtUp": "You're all caught up. More little extras soon.",
"selectCity": "Select City",
"searchCities": "Search cities",
"amuLastThreeMonths": "AMU (Last 3 months)",
"buyerDetails": "Buyer Details",
"chatBot": "ChatBot",
"helloAskMe": "Hello, ask me",
"anything": "anything...",
"thinking": "Thinking",
"waitingForResponse": "Waiting for response...",
"typeAMessage": "Type a message..",
"initializingChatPleaseWait": "Please wait, initializing chat...",
"am": "AM",
"pm": "PM"
} }

@ -1,699 +0,0 @@
#!/bin/bash
# Enhanced ELF Alignment Checker for Android 16KB Page Size Compatibility
# This script checks if your app's native libraries are compatible with 16KB page size devices
# as required by Google Play starting November 1st, 2025 for apps targeting Android 15+
progname="${0##*/}"
progname="${progname%.sh}"
# Color codes and formatting - detect if colors are supported
if [[ -t 1 ]] && command -v tput >/dev/null 2>&1 && tput colors >/dev/null 2>&1 && [[ $(tput colors) -ge 8 ]]; then
RED="\033[31m"
GREEN="\033[32m"
YELLOW="\033[33m"
BLUE="\033[34m"
CYAN="\033[36m"
PURPLE="\033[35m"
BOLD="\033[1m"
DIM="\033[2m"
ENDCOLOR="\033[0m"
USE_COLORS=true
else
# No color support - use plain text
RED=""
GREEN=""
YELLOW=""
BLUE=""
CYAN=""
PURPLE=""
BOLD=""
DIM=""
ENDCOLOR=""
USE_COLORS=false
fi
# Unicode symbols with fallbacks
if [[ $USE_COLORS == true ]]; then
CHECK_MARK="✓"
CROSS_MARK="✗"
WARNING_MARK="⚠"
INFO_MARK="ℹ"
ROCKET="🚀"
GEAR="⚙"
PACKAGE="📦"
DOCUMENT="📄"
ALERT="🚨"
PARTY="🎉"
TOOLS="🔧"
BOOKS="📚"
ARROW="▶"
else
CHECK_MARK="[OK]"
CROSS_MARK="[X]"
WARNING_MARK="[!]"
INFO_MARK="[i]"
ROCKET="[>]"
GEAR="[*]"
PACKAGE="[P]"
DOCUMENT="[D]"
ALERT="[!]"
PARTY="[*]"
TOOLS="[T]"
BOOKS="[B]"
ARROW=">"
fi
# Enhanced formatting functions
print_banner() {
local title="$1"
local subtitle="$2"
local width=88
echo
printf "%*s\n" $width | tr ' ' '═'
# Main title
local title_len=${#title}
local title_padding=$(( (width - title_len - 4) / 2 ))
printf "║${BOLD}${BLUE}%*s %s %*s${ENDCOLOR}║\n" \
$title_padding "" "$title" $title_padding ""
# Subtitle if provided
if [[ -n "$subtitle" ]]; then
local subtitle_len=${#subtitle}
local subtitle_padding=$(( (width - subtitle_len - 4) / 2 ))
printf "║${DIM}%*s %s %*s${ENDCOLOR}║\n" \
$subtitle_padding "" "$subtitle" $subtitle_padding ""
fi
printf "%*s\n" $width | tr ' ' '═'
echo
}
print_section() {
local icon="$1"
local title="$2"
local description="$3"
echo
echo -e "${BOLD}${BLUE}$icon $title${ENDCOLOR}"
if [[ -n "$description" ]]; then
echo -e "${DIM}$description${ENDCOLOR}"
fi
printf "${BLUE}%*s${ENDCOLOR}\n" 80 | tr ' ' '─'
}
print_status() {
local status="$1"
local message="$2"
local detail="$3"
case $status in
"success")
printf " ${GREEN}${CHECK_MARK}${ENDCOLOR} %s" "$message"
;;
"error")
printf " ${RED}${CROSS_MARK}${ENDCOLOR} %s" "$message"
;;
"warning")
printf " ${YELLOW}${WARNING_MARK}${ENDCOLOR} %s" "$message"
;;
"info")
printf " ${CYAN}${INFO_MARK}${ENDCOLOR} %s" "$message"
;;
"processing")
printf " ${PURPLE}${GEAR}${ENDCOLOR} %s" "$message"
;;
esac
if [[ -n "$detail" ]]; then
printf "\n ${DIM}%s${ENDCOLOR}" "$detail"
fi
echo
}
print_subsection() {
local title="$1"
echo
echo -e " ${BOLD}${PURPLE}${ARROW} $title${ENDCOLOR}"
printf " ${PURPLE}%*s${ENDCOLOR}\n" 50 | tr ' ' '┈'
}
print_table_header() {
echo
local line1="┌$(printf '─%.0s' {1..29})┬$(printf '─%.0s' {1..18})┬$(printf '─%.0s' {1..15})┬$(printf '─%.0s' {1..13})┐"
local line2="│ $(printf '%-27s' 'Library') │ $(printf '%-16s' 'Architecture') │ $(printf '%-13s' 'Alignment') │ $(printf '%-11s' 'Status') │"
local line3="├$(printf '─%.0s' {1..29})┼$(printf '─%.0s' {1..18})┼$(printf '─%.0s' {1..15})┼$(printf '─%.0s' {1..13})┤"
echo -e "${CYAN}$line1${ENDCOLOR}"
echo -e "${BOLD}${CYAN}$line2${ENDCOLOR}"
echo -e "${CYAN}$line3${ENDCOLOR}"
}
print_table_row() {
local lib="$1"
local arch="$2"
local alignment="$3"
local status="$4"
local is_critical="$5"
local status_symbol
local status_color
local status_text
if [[ $status == "ALIGNED" ]]; then
status_symbol="$CHECK_MARK"
status_color="$GREEN"
status_text="PASS"
else
status_symbol="$CROSS_MARK"
if [[ $is_critical == "true" ]]; then
status_color="$RED"
status_text="FAIL"
else
status_color="$YELLOW"
status_text="WARN"
fi
fi
# Truncate library name if too long
local display_lib="$lib"
if [[ ${#lib} -gt 27 ]]; then
display_lib="${lib:0:24}..."
fi
printf "${CYAN}│${ENDCOLOR} %-27s ${CYAN}│${ENDCOLOR} %-16s ${CYAN}│${ENDCOLOR} %-13s ${CYAN}│${ENDCOLOR} ${status_color}%s %-7s${ENDCOLOR} ${CYAN}│${ENDCOLOR}\n" \
"$display_lib" "$arch" "$alignment" "$status_symbol" "$status_text"
if [[ $is_critical == "true" && $status == "UNALIGNED" ]]; then
printf "${CYAN}│${ENDCOLOR} ${RED}%-75s${ENDCOLOR} ${CYAN}│${ENDCOLOR}\n" " ${ALERT} CRITICAL: Required for Google Play compliance!"
fi
}
print_table_footer() {
local line="└$(printf '─%.0s' {1..29})┴$(printf '─%.0s' {1..18})┴$(printf '─%.0s' {1..15})┴$(printf '─%.0s' {1..13})┘"
echo -e "${CYAN}$line${ENDCOLOR}"
echo
}
print_summary_box() {
local title="$1"
local status="$2" # success, warning, error
shift 2
local lines=("$@")
local box_color
local title_color
case $status in
"success") box_color="$GREEN"; title_color="$GREEN" ;;
"warning") box_color="$YELLOW"; title_color="$YELLOW" ;;
"error") box_color="$RED"; title_color="$RED" ;;
*) box_color="$BLUE"; title_color="$BLUE" ;;
esac
local width=78
echo
printf "${box_color}┌%*s┐${ENDCOLOR}\n" $((width-2)) | tr ' ' '─'
# Title
local title_len=${#title}
local title_padding=$(( (width - title_len - 4) / 2 ))
printf "${box_color}│${ENDCOLOR}${BOLD}${title_color}%*s %s %*s${ENDCOLOR}${box_color}│${ENDCOLOR}\n" \
$title_padding "" "$title" $title_padding ""
printf "${box_color}├%*s┤${ENDCOLOR}\n" $((width-2)) | tr ' ' '─'
# Content lines
for line in "${lines[@]}"; do
printf "${box_color}│${ENDCOLOR} %-*s ${box_color}│${ENDCOLOR}\n" $((width-4)) "$line"
done
printf "${box_color}└%*s┘${ENDCOLOR}\n" $((width-2)) | tr ' ' '─'
echo
}
cleanup_trap() {
if [ -n "${tmp}" -a -d "${tmp}" ]; then
print_status "processing" "Cleaning up temporary files..."
rm -rf "${tmp}"
fi
exit $1
}
usage() {
print_banner "Android 16KB Page Size Compatibility Checker" "Google Play Compliance Tool"
echo -e "${BOLD}DESCRIPTION:${ENDCOLOR}"
echo " This tool verifies that your Android app's native libraries are compatible"
echo " with 16KB page size devices, as required by Google Play starting November 1st, 2025."
echo
echo -e "${BOLD}USAGE:${ENDCOLOR}"
echo -e " ${GREEN}$progname${ENDCOLOR} ${CYAN}[input-path|input-APK|input-APEX]${ENDCOLOR}"
echo
echo -e "${BOLD}EXAMPLES:${ENDCOLOR}"
echo -e " ${DIM}# Check an APK file${ENDCOLOR}"
echo -e " $progname ${CYAN}app/build/outputs/apk/release/app-release.apk${ENDCOLOR}"
echo
echo -e " ${DIM}# Check a directory of native libraries${ENDCOLOR}"
echo -e " $progname ${CYAN}/path/to/native/libs/${ENDCOLOR}"
echo
echo -e "${BOLD}WHAT THIS TOOL CHECKS:${ENDCOLOR}"
echo -e " ${CHECK_MARK} APK zip-alignment for 16KB boundaries (requires build-tools 35.0.0+)"
echo -e " ${CHECK_MARK} ELF segment alignment in native libraries (arm64-v8a and x86_64)"
echo -e " ${CHECK_MARK} Compliance with Android 16KB page size requirements"
echo
echo -e "${BOLD}RESULT MEANINGS:${ENDCOLOR}"
echo -e " ${GREEN}${CHECK_MARK} PASS${ENDCOLOR} - Library is compatible with 16KB page sizes (2**14 or higher)"
echo -e " ${RED}${CROSS_MARK} FAIL${ENDCOLOR} - Library needs recompilation with 16KB ELF alignment"
echo -e " ${YELLOW}${WARNING_MARK} WARN${ENDCOLOR} - Non-critical architecture but should be fixed"
echo
}
# Enhanced dependency checking
check_dependencies() {
print_section "$GEAR" "Dependency Check" "Verifying required tools are available"
local missing_tools=()
local available_tools=()
# Check each tool
local tools=("objdump" "unzip" "file")
for tool in "${tools[@]}"; do
if command -v "$tool" >/dev/null 2>&1; then
available_tools+=("$tool")
print_status "success" "$tool" "$(which "$tool")"
else
missing_tools+=("$tool")
print_status "error" "$tool not found"
fi
done
if [ ${#missing_tools[@]} -gt 0 ]; then
echo
print_subsection "Installation Instructions"
for tool in "${missing_tools[@]}"; do
case $tool in
objdump)
print_status "info" "Install objdump:" "macOS: Xcode Command Line Tools or Android NDK's llvm-objdump"
print_status "info" "" "Linux: sudo apt-get install binutils (Ubuntu/Debian)"
;;
unzip)
print_status "info" "Install unzip:" "Usually pre-installed on most systems"
print_status "info" "" "Linux: sudo apt-get install unzip"
;;
file)
print_status "info" "Install file:" "Usually pre-installed on most systems"
print_status "info" "" "Linux: sudo apt-get install file"
;;
esac
echo
done
exit 1
fi
print_status "success" "All dependencies satisfied" "${#available_tools[@]} tools available"
}
# Validate input arguments
if [ ${#} -ne 1 ]; then
usage
exit 1
fi
case ${1} in
--help | -h | -\?)
usage
exit 0
;;
*)
dir="${1}"
;;
esac
# Validate input file/directory
if ! [ -f "${dir}" -o -d "${dir}" ]; then
print_status "error" "Invalid input: ${dir}"
echo " Please provide a valid APK file, APEX file, or directory containing native libraries."
exit 1
fi
# Check dependencies before proceeding
check_dependencies
print_banner "ANALYSIS IN PROGRESS" "Checking 16KB Page Size Compatibility"
print_status "processing" "Target: $(basename "${dir}")"
print_status "info" "Compliance Deadline: November 1st, 2025"
print_status "info" "Requirement: Apps targeting Android 15+ must support 16KB pages"
# APK Processing
if [[ "${dir}" == *.apk ]]; then
trap 'cleanup_trap' EXIT
print_section "$PACKAGE" "APK Analysis" "Processing Android Package file"
# Enhanced zipalign check
if command -v zipalign >/dev/null 2>&1; then
if { zipalign --help 2>&1 | grep -q "\-P <pagesize_kb>"; }; then
print_status "processing" "Checking APK zip-alignment for 16KB boundaries..."
zip_result=$(zipalign -v -c -P 16 4 "${dir}" 2>&1)
zip_exit_code=$?
if [ $zip_exit_code -eq 0 ]; then
print_status "success" "APK zip-alignment verification passed"
else
print_status "error" "APK zip-alignment verification failed"
echo " ${DIM}Details:${ENDCOLOR}"
echo "$zip_result" | grep -E 'lib/arm64-v8a|lib/x86_64|Verification|would be' | sed 's/^/ /' || echo " $zip_result"
fi
else
print_status "warning" "zipalign version doesn't support 16KB alignment checks"
print_status "info" "Solution: Update to Android SDK build-tools 35.0.0+"
echo " ${DIM}Update via Android Studio → SDK Manager → SDK Tools${ENDCOLOR}"
echo " ${DIM}Or run: sdkmanager \"build-tools;35.0.0\"${ENDCOLOR}"
fi
else
print_status "warning" "zipalign not found in PATH"
print_status "info" "Ensure Android SDK build-tools are installed and in PATH"
fi
# Extract APK
dir_filename=$(basename "${dir}")
tmp=$(mktemp -d -t "${dir_filename%.apk}_out_XXXXX")
if [ ! -d "${tmp}" ]; then
print_status "error" "Failed to create temporary directory"
exit 1
fi
print_status "processing" "Extracting native libraries from APK..."
if ! unzip -q "${dir}" "lib/*" -d "${tmp}" 2>/dev/null; then
print_summary_box "NO NATIVE LIBRARIES FOUND" "success" \
"${PARTY} Your app contains only Java/Kotlin code!" \
"" \
"${CHECK_MARK} Apps without native libraries automatically support 16KB devices" \
"${CHECK_MARK} No additional changes required for Google Play compliance" \
"${CHECK_MARK} You're all set for the November 1st, 2025 deadline!"
cleanup_trap 0
fi
dir="${tmp}"
fi
# APEX Processing
if [[ "${dir}" == *.apex ]]; then
trap 'cleanup_trap' EXIT
print_section "$DOCUMENT" "APEX Analysis" "Processing Android Pony EXpress file"
if ! command -v deapexer >/dev/null 2>&1; then
print_status "error" "deapexer tool not found"
echo " Please ensure Android SDK tools are properly installed and in your PATH."
exit 1
fi
dir_filename=$(basename "${dir}")
tmp=$(mktemp -d -t "${dir_filename%.apex}_out_XXXXX")
if [ ! -d "${tmp}" ]; then
print_status "error" "Failed to create temporary directory"
exit 1
fi
print_status "processing" "Extracting APEX contents..."
if ! deapexer extract "${dir}" "${tmp}"; then
print_status "error" "Failed to extract APEX file"
cleanup_trap 1
fi
dir="${tmp}"
fi
# Track libraries for enhanced summary
unaligned_libs=()
aligned_libs=()
critical_unaligned_libs=()
non_critical_unaligned_libs=()
total_libs=0
critical_libs=0
print_section "$GEAR" "ELF Segment Analysis" "Scanning native libraries for 16KB alignment compliance"
# Find all native libraries
matches="$(find "${dir}" -type f -name '*.so' 2>/dev/null)"
if [ -z "$matches" ]; then
# Also check for ELF files without .so extension
matches="$(find "${dir}" -type f -exec file {} \; 2>/dev/null | grep 'ELF' | cut -d: -f1)"
fi
if [ -z "$matches" ]; then
print_summary_box "NO NATIVE LIBRARIES DETECTED" "success" \
"${PARTY} Your app uses only Java/Kotlin code!" \
"" \
"${CHECK_MARK} No native library alignment issues to worry about" \
"${CHECK_MARK} Already compatible with 16KB page size devices" \
"${CHECK_MARK} Ready for Google Play's November 1st, 2025 requirement!"
cleanup_trap 0
fi
print_status "info" "Found native libraries - analyzing ELF segment alignment..."
print_table_header
IFS=$'\n'
for match in $matches; do
# Skip non-ELF files and nested packages
if [[ "${match}" == *".apk" ]]; then
continue
fi
if [[ "${match}" == *".apex" ]]; then
continue
fi
# Verify it's actually an ELF file
if ! [[ $(file "${match}" 2>/dev/null) == *"ELF"* ]]; then
continue
fi
total_libs=$((total_libs + 1))
# Determine architecture and criticality
arch="unknown"
is_critical="false"
if [[ "${match}" == *"arm64-v8a"* ]]; then
arch="arm64-v8a"
is_critical="true"
critical_libs=$((critical_libs + 1))
elif [[ "${match}" == *"x86_64"* ]]; then
arch="x86_64"
is_critical="true"
critical_libs=$((critical_libs + 1))
elif [[ "${match}" == *"armeabi-v7a"* ]]; then
arch="armeabi-v7a"
elif [[ "${match}" == *"x86"* ]]; then
arch="x86"
fi
# Check ELF segment alignment
res="$(objdump -p "${match}" 2>/dev/null | grep LOAD | awk '{ print $NF }' | head -1)"
if [ -z "$res" ]; then
print_table_row "$(basename "${match}")" "$arch" "UNKNOWN" "FAILED" "$is_critical"
continue
fi
# Check if alignment meets 16KB requirement (2**14 or higher)
if [[ $res =~ 2\*\*(1[4-9]|[2-9][0-9]|[1-9][0-9]{2,}) ]]; then
print_table_row "$(basename "${match}")" "$arch" "$res" "ALIGNED" "$is_critical"
aligned_libs+=("${match}")
else
print_table_row "$(basename "${match}")" "$arch" "$res" "UNALIGNED" "$is_critical"
unaligned_libs+=("${match}")
if [[ "$is_critical" == "true" ]]; then
critical_unaligned_libs+=("${match}")
else
non_critical_unaligned_libs+=("${match}")
fi
fi
done
print_table_footer
# Enhanced Summary Section
if [ ${#unaligned_libs[@]} -gt 0 ]; then
# Failure case - unaligned libraries found
summary_lines=(
"${ALERT} ACTION REQUIRED: Unaligned native libraries detected!"
""
"Analysis Results:"
" • Total libraries scanned: $total_libs"
" • Critical architectures (64-bit): $critical_libs"
" • Libraries aligned: ${#aligned_libs[@]}"
" • Libraries UNALIGNED: ${#unaligned_libs[@]}"
)
if [ ${#critical_unaligned_libs[@]} -gt 0 ]; then
summary_lines+=(" • Critical failures: ${#critical_unaligned_libs[@]} (MUST FIX)")
fi
if [ ${#non_critical_unaligned_libs[@]} -gt 0 ]; then
summary_lines+=(" • Non-critical warnings: ${#non_critical_unaligned_libs[@]} (SHOULD FIX)")
fi
summary_lines+=("")
summary_lines+=("Google Play Compliance: FAILED")
summary_lines+=("Deadline: November 1st, 2025")
print_summary_box "COMPATIBILITY CHECK FAILED" "error" "${summary_lines[@]}"
# Detailed library breakdown
if [ ${#critical_unaligned_libs[@]} -gt 0 ]; then
print_subsection "Critical Libraries Requiring Immediate Attention"
for lib in "${critical_unaligned_libs[@]}"; do
arch_type="64-bit"
[[ "${lib}" == *"arm64-v8a"* ]] && arch_type="ARM64"
[[ "${lib}" == *"x86_64"* ]] && arch_type="x86_64"
print_status "error" "$(basename "$lib")" "$arch_type architecture - Required for Google Play"
done
fi
if [ ${#non_critical_unaligned_libs[@]} -gt 0 ]; then
print_subsection "Non-Critical Libraries (Recommended to Fix)"
for lib in "${non_critical_unaligned_libs[@]}"; do
arch_type="32-bit"
[[ "${lib}" == *"armeabi-v7a"* ]] && arch_type="ARMv7"
[[ "${lib}" == *"x86"* ]] && arch_type="x86"
print_status "warning" "$(basename "$lib")" "$arch_type architecture"
done
fi
# Comprehensive fix instructions
print_section "$TOOLS" "How to Fix Unaligned Libraries" "Step-by-step guide to achieve 16KB compatibility"
print_subsection "Step 1: Update Your Build Environment"
print_status "info" "Android Gradle Plugin (AGP)" "Upgrade to version 8.5.1 or higher"
print_status "info" "Android NDK" "Update to NDK r27 or higher (r28+ recommended)"
print_status "info" "Build Tools" "Ensure Android SDK build-tools 35.0.0+ for zipalign"
print_subsection "Step 2: Configure 16KB ELF Alignment"
echo
echo -e "${BOLD}For NDK r28 and newer:${ENDCOLOR}"
print_status "success" "Automatic Support" "16KB alignment enabled by default - no changes needed!"
echo
echo -e "${BOLD}For NDK r27:${ENDCOLOR}"
print_status "info" "Gradle Configuration" "Add to your app/build.gradle:"
echo -e "${DIM} android {"
echo -e " defaultConfig {"
echo -e " externalNativeBuild {"
echo -e " cmake {"
echo -e " arguments '-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON'"
echo -e " }"
echo -e " }"
echo -e " }"
echo -e " }${ENDCOLOR}"
print_status "info" "NDK-Build Configuration" "Add to Application.mk:"
echo -e "${DIM} APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true${ENDCOLOR}"
echo
echo -e "${BOLD}For NDK r26 and older:${ENDCOLOR}"
print_status "info" "Manual Linker Flags" "Add to your native build configuration:"
echo -e "${DIM} # For Android.mk:"
echo -e " LOCAL_LDFLAGS += \"-Wl,-z,max-page-size=16384\""
echo
echo -e " # For CMakeLists.txt:"
echo -e " target_link_options(\${CMAKE_PROJECT_NAME} PRIVATE \"-Wl,-z,max-page-size=16384\")${ENDCOLOR}"
print_subsection "Step 3: Update Library Packaging"
print_status "info" "AGP 8.5.1+" "Uncompressed libraries used by default (no changes needed)"
print_status "info" "Older AGP versions" "Add to app/build.gradle:"
echo -e "${DIM} android {"
echo -e " packagingOptions {"
echo -e " jniLibs {"
echo -e " useLegacyPackaging = true"
echo -e " }"
echo -e " }"
echo -e " }${ENDCOLOR}"
print_subsection "Step 4: Build and Verify"
print_status "processing" "Clean Build" "Run ./gradlew clean"
print_status "processing" "Rebuild Project" "Run ./gradlew assembleRelease"
print_status "processing" "Re-run This Script" "Verify all libraries are now aligned"
print_status "processing" "Test on Device" "Use Android 15 emulator with 16KB system image"
print_section "$BOOKS" "Additional Resources" "Learn more about 16KB page size support"
print_status "info" "Official Guide" "https://developer.android.com/guide/practices/page-sizes"
print_status "info" "NDK Documentation" "https://developer.android.com/ndk/guides/"
print_status "info" "Testing Guide" "Use 'adb shell getconf PAGE_SIZE' (should return 16384)"
print_status "info" "Emulator Setup" "Android 15 system images with 16KB page size"
final_message=""
if [ ${#critical_unaligned_libs[@]} -gt 0 ]; then
final_message="CRITICAL: Fix required for Google Play compliance by November 1st, 2025!"
else
final_message="Warning: Non-critical issues found - recommended to fix for complete compatibility"
fi
print_summary_box "NEXT STEPS" "error" \
"$final_message" \
"" \
"1. Update your build tools and NDK version" \
"2. Apply the configuration changes above" \
"3. Clean and rebuild your project" \
"4. Run this script again to verify fixes" \
"5. Test thoroughly on 16KB devices/emulator"
cleanup_trap 1
else
# Success case - all libraries aligned
summary_lines=(
"${PARTY} All native libraries are properly aligned!"
""
"Analysis Results:"
" • Total libraries scanned: $total_libs"
" • Critical architectures: $critical_libs"
" • All libraries: ${#aligned_libs[@]}/${total_libs} ALIGNED"
""
"Google Play Compliance: PASSED ${CHECK_MARK}"
"Ready for November 1st, 2025 deadline!"
)
print_summary_box "COMPATIBILITY CHECK PASSED" "success" "${summary_lines[@]}"
print_section "$ROCKET" "Next Steps" "Ensure complete 16KB compatibility"
print_status "success" "Library Alignment" "All ELF segments properly aligned for 16KB pages"
print_status "info" "Runtime Testing" "Test on Android 15 emulator with 16KB system image"
print_status "info" "Code Review" "Check for hardcoded PAGE_SIZE dependencies in your code"
print_status "info" "Third-party SDKs" "Verify all dependencies are 16KB compatible"
print_status "info" "Zipalign Verification" "Run: zipalign -c -P 16 -v 4 your-app.apk"
print_subsection "Testing Commands"
echo -e "${DIM} # Verify page size on device/emulator${ENDCOLOR}"
echo -e "${DIM} adb shell getconf PAGE_SIZE # Should return: 16384${ENDCOLOR}"
echo
echo -e "${DIM} # Verify APK alignment${ENDCOLOR}"
echo -e "${DIM} zipalign -c -P 16 -v 4 app-release.apk${ENDCOLOR}"
print_summary_box "CONGRATULATIONS!" "success" \
"${PARTY} Your app is 16KB page size compatible!" \
"" \
"${CHECK_MARK} All native libraries meet Google Play requirements" \
"${CHECK_MARK} Ready for devices with 16KB page sizes" \
"${CHECK_MARK} Compliant with November 1st, 2025 deadline" \
"" \
"Recommendation: Test thoroughly on 16KB environment to ensure" \
"no runtime issues exist in your application code."
fi
echo
print_status "info" "Analysis completed at $(date)"
echo

@ -21,6 +21,6 @@
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>13.0</string> <string>12.0</string>
</dict> </dict>
</plist> </plist>

@ -3,16 +3,15 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 60; objectVersion = 54;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
0AD79F8B1905C17FADC8B42C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8F0CC8EAC13AF3E07BEE567 /* Pods_Runner.framework */; };
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
1CCF895CFB3C95DEBB84A05F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8F0CC8EAC13AF3E07BEE567 /* Pods_Runner.framework */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
76AE113C28E078EA00513112 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 76AE113B28E078EA00513112 /* GoogleService-Info.plist */; }; 76AE113C28E078EA00513112 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 76AE113B28E078EA00513112 /* GoogleService-Info.plist */; };
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
@ -40,7 +39,6 @@
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7627313328E45A2400F354F7 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; }; 7627313328E45A2400F354F7 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
76AE113B28E078EA00513112 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; }; 76AE113B28E078EA00513112 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
@ -59,7 +57,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
0AD79F8B1905C17FADC8B42C /* Pods_Runner.framework in Frameworks */, 1CCF895CFB3C95DEBB84A05F /* Pods_Runner.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -77,7 +75,6 @@
9740EEB11CF90186004384FC /* Flutter */ = { 9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */, 9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
@ -154,9 +151,6 @@
dependencies = ( dependencies = (
); );
name = Runner; name = Runner;
packageProductDependencies = (
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
);
productName = Runner; productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
@ -185,9 +179,6 @@
Base, Base,
); );
mainGroup = 97C146E51CF9000F007C117D; mainGroup = 97C146E51CF9000F007C117D;
packageReferences = (
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
);
productRefGroup = 97C146EF1CF9000F007C117D /* Products */; productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
@ -251,6 +242,23 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
}; };
6C6E76E4FB19FAF11C24005D /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = { 9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1; alwaysOutOfDate = 1;
@ -283,23 +291,6 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
D9FFC0F21D4146458615A992 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
@ -376,7 +367,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@ -463,7 +454,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -513,7 +504,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@ -609,20 +600,6 @@
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCLocalSwiftPackageReference section */
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
};
/* End XCLocalSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
isa = XCSwiftPackageProductDependency;
productName = FlutterGeneratedPluginSwiftPackage;
};
/* End XCSwiftPackageProductDependency section */
}; };
rootObject = 97C146E61CF9000F007C117D /* Project object */; rootObject = 97C146E61CF9000F007C117D /* Project object */;
} }

@ -3,41 +3,9 @@
LastUpgradeVersion = "1510" LastUpgradeVersion = "1510"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "NO" parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"> buildImplicitDependencies = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Prepare Flutter Framework Script"
scriptText = "/bin/sh &quot;$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh&quot; prepare&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
<BuildActionEntries> <BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8B74B458B450D74B75744B87BD747314"
BuildableName = "Pods_Runner.framework"
BlueprintName = "Pods-Runner"
ReferencedContainer = "container:Pods/Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry <BuildActionEntry
buildForTesting = "YES" buildForTesting = "YES"
buildForRunning = "YES" buildForRunning = "YES"

@ -3,8 +3,9 @@ import Flutter
import Firebase import Firebase
import flutter_local_notifications import flutter_local_notifications
@main @main
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { @objc class AppDelegate: FlutterAppDelegate {
override func application( override func application(
_ application: UIApplication, _ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
@ -16,11 +17,7 @@ import flutter_local_notifications
if #available(iOS 10.0, *) { if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
} }
// GeneratedPluginRegistrant.register(with: self) GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions) return super.application(application, didFinishLaunchingWithOptions: launchOptions)
} }
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
}
} }

@ -2,27 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneDelegateClassName</key>
<string>FlutterSceneDelegate</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>CADisableMinimumFrameDurationOnPhone</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<true/> <true/>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
@ -52,11 +31,6 @@
<string>sms</string> <string>sms</string>
<string>tel</string> <string>tel</string>
<string>mailto</string> <string>mailto</string>
<string>https</string>
<string>http</string>
<string>comgooglemaps</string>
<string>googlechromes</string>
<string>waze</string>
</array> </array>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
@ -72,23 +46,19 @@
<true/> <true/>
</dict> </dict>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>This app requires camera access to capture &amp; upload picture as profile image, and scan QR codes.</string> <string>This app requires camera access to capture &amp; upload picture as profile image.</string>
<key>NSFaceIDUsageDescription</key> <key>NSFaceIDUsageDescription</key>
<string>This app requires Face ID to allow biometric authentication for app login.</string> <string>This app requires Face ID to allow biometric authentication for app login.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>This app requires access to local network to connect to company Wi-Fi networks for attendance.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key> <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app requires access to your location to mark your attendance and discover nearby offers and merchants.</string> <string>This App requires access to your location to mark your attendance.</string>
<key>NSLocationAlwaysUsageDescription</key> <key>NSLocationAlwaysUsageDescription</key>
<string>This app requires access to your location to mark your attendance and discover nearby offers and merchants.</string> <string>This App requires access to your location to mark your attendance.</string>
<key>NSLocationWhenInUseUsageDescription</key> <key>NSLocationWhenInUseUsageDescription</key>
<string>This app requires access to your location to mark your attendance and discover nearby offers and merchants.</string> <string>This App requires access to your location to mark your attendance.</string>
<key>NSMicrophoneUsageDescription</key> <key>NSMicrophoneUsageDescription</key>
<string>This app requires microphone access for calls and speech-to-text input.</string> <string>This app requires microphone access to for call.</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>This app requires photo library access to select image as document &amp; upload it.</string> <string>This app requires photo library access to select image as document &amp; upload it.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>This app requires speech recognition to convert your voice to text.</string>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<key>NSSpeechRecognitionUsageDescription</key> <key>NSSpeechRecognitionUsageDescription</key>

@ -20,7 +20,6 @@
<true/> <true/>
<key>com.apple.developer.nfc.readersession.formats</key> <key>com.apple.developer.nfc.readersession.formats</key>
<array> <array>
<string>NDEF</string>
<string>TAG</string> <string>TAG</string>
</array> </array>
<key>com.apple.developer.ubiquity-container-identifiers</key> <key>com.apple.developer.ubiquity-container-identifiers</key>

@ -1,9 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:http/io_client.dart'; import 'package:http/io_client.dart';
@ -21,10 +19,14 @@ class APIError {
int? errorType; int? errorType;
String? errorMessage; String? errorMessage;
int? errorStatusCode; int? errorStatusCode;
APIError(this.errorCode, this.errorMessage, this.errorType, this.errorStatusCode); APIError(this.errorCode, this.errorMessage, this.errorType, this.errorStatusCode);
Map<String, dynamic> toJson() => {'errorCode': errorCode, 'errorMessage': errorMessage, 'errorType': errorType, 'ErrorStatusCode': errorStatusCode}; Map<String, dynamic> toJson() => {
'errorCode': errorCode,
'errorMessage': errorMessage,
'errorType': errorType,
'ErrorStatusCode': errorStatusCode
};
@override @override
String toString() { String toString() {
@ -37,39 +39,31 @@ APIException _throwAPIException(Response response) {
case 200: case 200:
APIError? apiError; APIError? apiError;
if (response.body != null && response.body.isNotEmpty) { if (response.body != null && response.body.isNotEmpty) {
try {
var jsonError = jsonDecode(response.body); var jsonError = jsonDecode(response.body);
print(jsonError); print(jsonError);
apiError = APIError(jsonError['ErrorCode'], jsonError['ErrorMessage'], jsonError['ErrorType'], jsonError['ErrorStatusCode']); apiError = APIError(jsonError['ErrorCode'], jsonError['ErrorMessage'], jsonError['ErrorType'], jsonError['ErrorStatusCode']);
} catch (_) {
log("Non-JSON 200 error body: ${response.body}");
}
} }
return APIException(APIException.BAD_REQUEST, error: apiError); return APIException(APIException.BAD_REQUEST, error: apiError);
case 400: case 400:
APIError? apiError; APIError? apiError;
if (response.body != null && response.body.isNotEmpty) { if (response.body != null && response.body.isNotEmpty) {
try {
var jsonError = jsonDecode(response.body); var jsonError = jsonDecode(response.body);
apiError = APIError(jsonError['ErrorCode'], jsonError['ErrorMessage'], jsonError['ErrorType'], jsonError['ErrorStatusCode']); apiError = APIError(jsonError['ErrorCode'], jsonError['ErrorMessage'], jsonError['ErrorType'],jsonError['ErrorStatusCode']);
} catch (_) {
log("Non-JSON 400 error body: ${response.body}");
}
} }
return APIException(APIException.BAD_REQUEST, error: apiError); return APIException(APIException.BAD_REQUEST, error: apiError);
case 401: case 401:
return const APIException(APIException.UNAUTHORIZED); return APIException(APIException.UNAUTHORIZED);
case 403: case 403:
return const APIException(APIException.FORBIDDEN); return APIException(APIException.FORBIDDEN);
case 404: case 404:
return const APIException(APIException.NOT_FOUND); return APIException(APIException.NOT_FOUND);
case 500: case 500:
return const APIException(APIException.INTERNAL_SERVER_ERROR); return APIException(APIException.INTERNAL_SERVER_ERROR);
case 444: case 444:
var downloadUrl = response.headers["location"]; var downloadUrl = response.headers["location"];
return APIException(APIException.UPGRADE_REQUIRED, arguments: downloadUrl); return APIException(APIException.UPGRADE_REQUIRED, arguments: downloadUrl);
default: default:
return const APIException(APIException.OTHER); return APIException(APIException.OTHER);
} }
} }
@ -80,29 +74,22 @@ class ApiClient {
factory ApiClient() => _instance; factory ApiClient() => _instance;
Future<U> postJsonForObject<T, U>( Future<U> postJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url, T jsonObject,
FactoryConstructor<U> factoryConstructor, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0, bool isFormData = false}) async {
String url,
T jsonObject, {
String? token,
Map<String, dynamic>? queryParameters,
Map<String, String>? headers,
int retryTimes = 0,
bool isFormData = false,
}) async {
var _headers = {'Accept': 'application/json'}; var _headers = {'Accept': 'application/json'};
if (headers != null && headers.isNotEmpty) { if (headers != null && headers.isNotEmpty) {
_headers.addAll(headers); _headers.addAll(headers);
} }
if (!kReleaseMode) { if (!kReleaseMode) {
log("Url: $url"); print("Url:$url");
var bodyJson = json.encode(jsonObject); var bodyJson = json.encode(jsonObject);
log("body:$bodyJson"); print("body:$bodyJson");
} }
var response = await postJsonForResponse(url, jsonObject, token: token, queryParameters: queryParameters, headers: _headers, retryTimes: retryTimes, isFormData: isFormData); var response = await postJsonForResponse(url, jsonObject, token: token, queryParameters: queryParameters, headers: _headers, retryTimes: retryTimes, isFormData: isFormData);
try { try {
if (!kReleaseMode) { if (!kReleaseMode) {
log("res: " + response.body); logger.i("Url: " + url);
logger.i("res: " + response.body);
} }
var jsonData = jsonDecode(response.body); var jsonData = jsonDecode(response.body);
@ -129,21 +116,13 @@ class ApiClient {
if (ex is APIException) { if (ex is APIException) {
rethrow; rethrow;
} else { } else {
log("BAD_RESPONSE_FORMAT for $url -> body: ${response.body}");
throw APIException(APIException.BAD_RESPONSE_FORMAT, arguments: ex); throw APIException(APIException.BAD_RESPONSE_FORMAT, arguments: ex);
} }
} }
} }
Future<Response> postJsonForResponse<T>( Future<Response> postJsonForResponse<T>(String url, T jsonObject,
String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0, bool isFormData = false}) async {
T jsonObject, {
String? token,
Map<String, dynamic>? queryParameters,
Map<String, String>? headers,
int retryTimes = 0,
bool isFormData = false,
}) async {
String? requestBody; String? requestBody;
late Map<String, String> stringObj; late Map<String, String> stringObj;
if (jsonObject != null) { if (jsonObject != null) {
@ -178,9 +157,9 @@ class ApiClient {
var queryString = new Uri(queryParameters: queryParameters).query; var queryString = new Uri(queryParameters: queryParameters).query;
url = url + '?' + queryString; url = url + '?' + queryString;
} }
var response = await _post(Uri.parse(url), body: requestBody, headers: _headers).timeout(const Duration(seconds: 120)); var response = await _post(Uri.parse(url), body: requestBody, headers: _headers).timeout(Duration(seconds: 120));
if (response.statusCode >= 200 && response.statusCode < 300) { if (response. statusCode >= 200 && response.statusCode < 300) {
return response; return response;
} else { } else {
throw _throwAPIException(response); throw _throwAPIException(response);
@ -188,7 +167,7 @@ class ApiClient {
} on SocketException catch (e) { } on SocketException catch (e) {
if (retryTimes > 0) { if (retryTimes > 0) {
print('will retry after 3 seconds...'); print('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3)); await Future.delayed(Duration(seconds: 3));
return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1);
} else { } else {
throw APIException(APIException.OTHER, arguments: e); throw APIException(APIException.OTHER, arguments: e);
@ -196,7 +175,7 @@ class ApiClient {
} on HttpException catch (e) { } on HttpException catch (e) {
if (retryTimes > 0) { if (retryTimes > 0) {
print('will retry after 3 seconds...'); print('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3)); await Future.delayed(Duration(seconds: 3));
return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1);
} else { } else {
throw APIException(APIException.OTHER, arguments: e); throw APIException(APIException.OTHER, arguments: e);
@ -206,7 +185,7 @@ class ApiClient {
} on ClientException catch (e) { } on ClientException catch (e) {
if (retryTimes > 0) { if (retryTimes > 0) {
print('will retry after 3 seconds...'); print('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3)); await Future.delayed(Duration(seconds: 3));
return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1);
} else { } else {
throw APIException(APIException.OTHER, arguments: e); throw APIException(APIException.OTHER, arguments: e);
@ -245,7 +224,7 @@ class ApiClient {
var queryString = new Uri(queryParameters: queryParameters).query; var queryString = new Uri(queryParameters: queryParameters).query;
url = url + '?' + queryString; url = url + '?' + queryString;
} }
var response = await _get(Uri.parse(url), headers: _headers).timeout(const Duration(seconds: 60)); var response = await _get(Uri.parse(url), headers: _headers).timeout(Duration(seconds: 60));
if (response.statusCode >= 200 && response.statusCode < 300) { if (response.statusCode >= 200 && response.statusCode < 300) {
return response; return response;
@ -255,7 +234,7 @@ class ApiClient {
} on SocketException catch (e) { } on SocketException catch (e) {
if (retryTimes > 0) { if (retryTimes > 0) {
print('will retry after 3 seconds...'); print('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3)); await Future.delayed(Duration(seconds: 3));
return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1);
} else { } else {
throw APIException(APIException.OTHER, arguments: e); throw APIException(APIException.OTHER, arguments: e);
@ -263,7 +242,7 @@ class ApiClient {
} on HttpException catch (e) { } on HttpException catch (e) {
if (retryTimes > 0) { if (retryTimes > 0) {
print('will retry after 3 seconds...'); print('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3)); await Future.delayed(Duration(seconds: 3));
return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1);
} else { } else {
throw APIException(APIException.OTHER, arguments: e); throw APIException(APIException.OTHER, arguments: e);
@ -273,7 +252,7 @@ class ApiClient {
} on ClientException catch (e) { } on ClientException catch (e) {
if (retryTimes > 0) { if (retryTimes > 0) {
print('will retry after 3 seconds...'); print('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3)); await Future.delayed(Duration(seconds: 3));
return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1);
} else { } else {
throw APIException(APIException.OTHER, arguments: e); throw APIException(APIException.OTHER, arguments: e);

@ -1,11 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:mobile_device_identifier/mobile_device_identifier.dart';
import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/api/api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/consts.dart';
@ -15,7 +15,6 @@ import 'package:mohem_flutter_app/models/dashboard/get_accural_ticket_balance_mo
import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_model.dart'; import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_model.dart';
import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart'; import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart';
import 'package:mohem_flutter_app/models/dashboard/list_menu.dart'; import 'package:mohem_flutter_app/models/dashboard/list_menu.dart';
import 'package:mohem_flutter_app/models/eit_attachment_ticket_model.dart';
import 'package:mohem_flutter_app/models/dashboard/courses_response_model.dart'; import 'package:mohem_flutter_app/models/dashboard/courses_response_model.dart';
import 'package:mohem_flutter_app/models/generic_response_model.dart'; import 'package:mohem_flutter_app/models/generic_response_model.dart';
import 'package:mohem_flutter_app/models/get_employee_parking_details_model.dart'; import 'package:mohem_flutter_app/models/get_employee_parking_details_model.dart';
@ -23,7 +22,6 @@ import 'package:mohem_flutter_app/models/greetings/greeting_card_model.dart';
import 'package:mohem_flutter_app/models/itg/itg_main_response.dart'; import 'package:mohem_flutter_app/models/itg/itg_main_response.dart';
import 'package:mohem_flutter_app/models/itg/itg_response_model.dart'; import 'package:mohem_flutter_app/models/itg/itg_response_model.dart';
import 'package:mohem_flutter_app/models/sso_auth_model.dart'; import 'package:mohem_flutter_app/models/sso_auth_model.dart';
// import 'package:platform_device_id/platform_device_id.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
@ -142,7 +140,7 @@ class DashboardApiClient {
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject(
(json) { (json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json); GenericResponseModel responseData = GenericResponseModel.fromJson(json);
return responseData.listMenu ?? <ListMenu>[]; return responseData.listMenu ?? [];
}, },
url, url,
postParams, postParams,
@ -152,7 +150,7 @@ class DashboardApiClient {
//GET_MENU_ENTRIES //GET_MENU_ENTRIES
Future<GenericResponseModel?> getGetMenuEntries() async { Future<GenericResponseModel?> getGetMenuEntries() async {
String url = "${ApiConsts.erpRest}GET_MENU_ENTRIES"; String url = "${ApiConsts.erpRest}GET_MENU_ENTRIES";
Map<String, dynamic> postParams = <String, dynamic>{"P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E"}; Map<String, dynamic> postParams = {"P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E"};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject(
(json) { (json) {
@ -166,7 +164,7 @@ class DashboardApiClient {
Future<GenericResponseModel?> getEventActivity() async { Future<GenericResponseModel?> getEventActivity() async {
String url = "${ApiConsts.erpRest}Get_EventActivity"; String url = "${ApiConsts.erpRest}Get_EventActivity";
Map<String, dynamic> postParams = <String, dynamic>{"P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E"}; Map<String, dynamic> postParams = {"P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E"};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject(
(json) { (json) {
@ -180,7 +178,7 @@ class DashboardApiClient {
Future<GenericResponseModel?> getTicketBookingRedirection() async { Future<GenericResponseModel?> getTicketBookingRedirection() async {
String url = "${ApiConsts.erpRest}GET_PORTAL_REDIRECTION"; String url = "${ApiConsts.erpRest}GET_PORTAL_REDIRECTION";
Map<String, dynamic> postParams = <String, dynamic>{"P_USER_NAME": AppState().memberInformationList?.eMPLOYEENUMBER}; Map<String, dynamic> postParams = {"P_USER_NAME": AppState().memberInformationList?.eMPLOYEENUMBER};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject(
(json) { (json) {
@ -194,7 +192,7 @@ class DashboardApiClient {
Future<GenericResponseModel?> getTicketBalance() async { Future<GenericResponseModel?> getTicketBalance() async {
String url = "${ApiConsts.erpRest}GET_TICKET_BALANCE"; String url = "${ApiConsts.erpRest}GET_TICKET_BALANCE";
Map<String, dynamic> postParams = <String, dynamic>{"P_USER_NAME": AppState().memberInformationList?.eMPLOYEENUMBER}; Map<String, dynamic> postParams = {"P_USER_NAME": AppState().memberInformationList?.eMPLOYEENUMBER};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject(
@ -211,7 +209,7 @@ class DashboardApiClient {
String url = "${ApiConsts.ssoAuthRedirection}?grantType=mohemm"; String url = "${ApiConsts.ssoAuthRedirection}?grantType=mohemm";
//https://sso-uat.hmg.com/api/auth/connect?grantType=mohemm' //https://sso-uat.hmg.com/api/auth/connect?grantType=mohemm'
// Map<String, dynamic> postParams = {"P_USER_NAME": AppState().memberInformationList?.eMPLOYEENUMBER}; // Map<String, dynamic> postParams = {"P_USER_NAME": AppState().memberInformationList?.eMPLOYEENUMBER};
Map<String, dynamic> postParams = <String, dynamic>{ Map<String, dynamic> postParams = {
"ClientId": clientID, "ClientId": clientID,
// "ClientId": "a9f4d1a0596d4aea8f830992ec4bdac1", // "ClientId": "a9f4d1a0596d4aea8f830992ec4bdac1",
"PersonId": AppState().memberInformationList?.eMPLOYEENUMBER, "PersonId": AppState().memberInformationList?.eMPLOYEENUMBER,
@ -230,35 +228,20 @@ class DashboardApiClient {
); );
} }
Future<GetEitAttachmentTicket?> getTicketEitPDFDownload({required String peiExtraInfoId}) async { Future<dynamic> getBookingSSOFinalRedirection({required String token}) async {
String url = "${ApiConsts.erpRest}GET_EIT_ATTACHMENTS";
Map<String, dynamic> postParams = <String, dynamic>{
"P_PERSON_EXTRA_INFO_ID": peiExtraInfoId,
"PersonId": AppState().memberInformationList?.eMPLOYEENUMBER,
"Username": AppState().memberInformationList?.eMPLOYEENUMBER,
"Language": "US",
"SessionId": AppState().postParamsObject?.pSessionId,
};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
GetEitAttachmentTicket responseData = GetEitAttachmentTicket.fromJson(json);
return responseData;
},
url,
postParams,
);
}
Future<dynamic> getBookingSSOFinalRedirection({required String token, required String refreshToken}) async {
// token = // token =
// "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhjZTE2OWM0YjIwYjQ2ZWM5YTQyOTU3Y2ZhODUzNzQ1IiwidHlwIjoiSldUIn0.eyJ0ZW5hbnRfaWQiOiJhOWY0ZDFhMDU5NmQ0YWVhOGY4MzA5OTJlYzRiZGFjMSIsImVpZCI6IjExNzkzMCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3NpZCI6Ijk2MDI0OGM1NzA3YzQ3MmFhYTEzM2I1N2ZhODE1ZmVhIiwibGFuZ3VhZ2UiOiJVUyIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6IjExNzkzMEBobWcuY29tIiwiZXhwIjoxNzgyNDc1NzY5LCJpc3MiOiJodHRwczovL3Nzby11YXQuaG1nLmNvbSIsImF1ZCI6ImE5ZjRkMWEwNTk2ZDRhZWE4ZjgzMDk5MmVjNGJkYWMxIn0.rJcLVsG8D0XECyLERCTD2uqGeWyvp-OBVGE9uL2qKrX4etFUHgdFt_5kYF6edFTtGy-0PIZadHDmv7e-IOhVWHm5HVMClaukiXoRXR8cDN8XA1wfme3Kd-U5PXN-IRh49AyRTzLO0rYNPvH81ScosWGlsFSkOvA-0hJNa2adHdtvgNvB8wJshSU5p7sAmF8mjdDY6aInG19etu2iEuUDwHHA4ZY_ts4hboHo8fE392hFaYGonExoD7bpW5RMx5xKWeRCmWpG_PK8Aw_z1jGzdB9PANus4pteRGuln1J-kmo2lQC9pVrSyZATAKp1HfgfyZ_vUhaHEfM69cMWaCslJQ"; // "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhjZTE2OWM0YjIwYjQ2ZWM5YTQyOTU3Y2ZhODUzNzQ1IiwidHlwIjoiSldUIn0.eyJ0ZW5hbnRfaWQiOiJhOWY0ZDFhMDU5NmQ0YWVhOGY4MzA5OTJlYzRiZGFjMSIsImVpZCI6IjExNzkzMCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3NpZCI6Ijk2MDI0OGM1NzA3YzQ3MmFhYTEzM2I1N2ZhODE1ZmVhIiwibGFuZ3VhZ2UiOiJVUyIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6IjExNzkzMEBobWcuY29tIiwiZXhwIjoxNzgyNDc1NzY5LCJpc3MiOiJodHRwczovL3Nzby11YXQuaG1nLmNvbSIsImF1ZCI6ImE5ZjRkMWEwNTk2ZDRhZWE4ZjgzMDk5MmVjNGJkYWMxIn0.rJcLVsG8D0XECyLERCTD2uqGeWyvp-OBVGE9uL2qKrX4etFUHgdFt_5kYF6edFTtGy-0PIZadHDmv7e-IOhVWHm5HVMClaukiXoRXR8cDN8XA1wfme3Kd-U5PXN-IRh49AyRTzLO0rYNPvH81ScosWGlsFSkOvA-0hJNa2adHdtvgNvB8wJshSU5p7sAmF8mjdDY6aInG19etu2iEuUDwHHA4ZY_ts4hboHo8fE392hFaYGonExoD7bpW5RMx5xKWeRCmWpG_PK8Aw_z1jGzdB9PANus4pteRGuln1J-kmo2lQC9pVrSyZATAKp1HfgfyZ_vUhaHEfM69cMWaCslJQ";
http.MultipartRequest request = http.MultipartRequest('POST', Uri.parse('https://www.paxes.com/sso/hmg')); // // var request = http.MultipartRequest('POST', Uri.parse('https://ek.techmaster.in/SSO/HMG'));
// http.MultipartRequest request = http.MultipartRequest('POST', Uri.parse('https://ek.techmaster.in/SSO/HMG')); var request = http.MultipartRequest('POST', Uri.parse('https://Paxes-release.techmaster.in/SSO/HMG')); // UAT
request.fields.addAll(<String, String>{'JWTToken': token, 'RefreshToken': refreshToken}); // var request = http.MultipartRequest('POST', Uri.parse('https://paxes.com/sso/hmg')); // Prod
request.fields.addAll({'JWTToken': token});
// request.headers.addAll(headers);
http.StreamedResponse response = await request.send(); http.StreamedResponse response = await request.send();
if (response.statusCode == 302) { if (response.statusCode == 302) {
String res = await response.stream.bytesToString(); print("================== post ==========");
var res = await response.stream.bytesToString();
return response.headers["location"]; return response.headers["location"];
} else { } else {
print(response.reasonPhrase); print(response.reasonPhrase);
@ -275,22 +258,22 @@ class DashboardApiClient {
String QRValue = "", String QRValue = "",
String payrollCode = "", String payrollCode = "",
}) async { }) async {
var _mobileDeviceIdentifierPlugin = MobileDeviceIdentifier();
Codec<String, String> stringToBase64 = utf8.fuse(base64); Codec<String, String> stringToBase64 = utf8.fuse(base64);
String url = "${ApiConsts.swpRest}AuthenticateAndSwipeUserSupportNFC"; String url = "${ApiConsts.swpRest}AuthenticateAndSwipeUserSupportNFC";
Uuid uuid = Uuid(); var uuid = Uuid();
String? deviceId = ""; String? deviceId = "";
// Generate a v4 (random) id // Generate a v4 (random) id
if (Platform.isAndroid) { if (Platform.isAndroid) {
AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo; AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo;
deviceId = await androidInfo.id; deviceId = await _mobileDeviceIdentifierPlugin.getDeviceId() ?? androidInfo.id;
deviceId = stringToBase64.encode(deviceId); deviceId = stringToBase64.encode(deviceId);
deviceId = deviceId.length >= 50 ? deviceId.substring(0, 48) : deviceId; deviceId = deviceId.length >= 50 ? deviceId.substring(0, 48) : deviceId;
} else if (Platform.isIOS) { } else if (Platform.isIOS) {
IosDeviceInfo iosInfo = await DeviceInfoPlugin().iosInfo; IosDeviceInfo iosInfo = await DeviceInfoPlugin().iosInfo;
deviceId = iosInfo.identifierForVendor; deviceId = iosInfo.identifierForVendor;
} }
Map<String, dynamic> postParams = <String, dynamic>{ Map<String, dynamic> postParams = {
"UID": deviceId, //uuid.v4(), //Mobile Id "UID": deviceId, //uuid.v4(), //Mobile Id
// "UID": uuid.v4(), //Mobile Id // "UID": uuid.v4(), //Mobile Id
"Latitude": lat, "Latitude": lat,
@ -316,10 +299,10 @@ class DashboardApiClient {
//Mark Fake Location //Mark Fake Location
Future<GenericResponseModel?> markFakeLocation({String lat = "0", String? long = "0", required String sourceName}) async { Future<GenericResponseModel?> markFakeLocation({String lat = "0", String? long = "0", required String sourceName}) async {
String url = "${ApiConsts.swpRest}CreateIssueInfo"; String url = "${ApiConsts.swpRest}CreateIssueInfo";
Uuid uuid = Uuid(); var uuid = Uuid();
// Generate a v4 (random) id // Generate a v4 (random) id
Map<String, dynamic> postParams = <String, dynamic>{ Map<String, dynamic> postParams = {
"UID": uuid.v4(), //Mobile Id "UID": uuid.v4(), //Mobile Id
"Latitude": lat, "Latitude": lat,
"Longitude": long, "Longitude": long,
@ -343,7 +326,7 @@ class DashboardApiClient {
Future<MohemmItgResponseItem?> getITGPageNotification() async { Future<MohemmItgResponseItem?> getITGPageNotification() async {
String url = "${ApiConsts.cocRest}Mohemm_ITG_GetPageNotification"; String url = "${ApiConsts.cocRest}Mohemm_ITG_GetPageNotification";
Map<String, dynamic> postParams = <String, dynamic>{ Map<String, dynamic> postParams = {
"EmployeeNumber": AppState().getUserName, "EmployeeNumber": AppState().getUserName,
"ItgEnableAt": "After Service Submission", //Mobile Id "ItgEnableAt": "After Service Submission", //Mobile Id
"ItgServiceName": "Login", "ItgServiceName": "Login",
@ -361,26 +344,11 @@ class DashboardApiClient {
); );
} }
Future<String?> getENSATItgToken() async {
String url = "${ApiConsts.cocRest}Mohemm_ENSAT_GetItgToken";
Map<String, dynamic> postParams = <String, dynamic>{"EmployeeNumber": AppState().getUserName};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json);
return responseData.Mohemm_ENSAT_GetItgToken_Response;
},
url,
postParams,
);
}
//Submit ITG //Submit ITG
Future<ItgMainRes?> submitItgForm({required String comment, required String masterId, required List<Map<String, dynamic>> itgList, required int serviceId}) async { Future<ItgMainRes?> submitItgForm({required String comment, required String masterId, required List<Map<String, dynamic>> itgList, required int serviceId}) async {
String url = "${ApiConsts.cocRest}Mohemm_ITG_Survey_Response"; String url = "${ApiConsts.cocRest}Mohemm_ITG_Survey_Response";
Map<String, dynamic> postParams = <String, dynamic>{ Map<String, dynamic> postParams = {
"EmployeeNumber": AppState().getUserName, "EmployeeNumber": AppState().getUserName,
"ItgComments": comment, "ItgComments": comment,
"ItgNotificationMasterId": masterId, "ItgNotificationMasterId": masterId,
@ -401,7 +369,7 @@ class DashboardApiClient {
Future<ItgMainRes?> getAdvertisementDetail(String masterID) async { Future<ItgMainRes?> getAdvertisementDetail(String masterID) async {
String url = "${ApiConsts.cocRest}Mohemm_ITG_GetPageNotificationDetails"; String url = "${ApiConsts.cocRest}Mohemm_ITG_GetPageNotificationDetails";
Map<String, dynamic> postParams = <String, dynamic>{ Map<String, dynamic> postParams = {
"EmployeeNumber": AppState().getUserName, "EmployeeNumber": AppState().getUserName,
"ItgNotificationMasterId": masterID, //Mobile Id "ItgNotificationMasterId": masterID, //Mobile Id
}; };
@ -419,7 +387,7 @@ class DashboardApiClient {
Future setAdvertisementViewed(String masterID, int advertisementId, String? ackValue) async { Future setAdvertisementViewed(String masterID, int advertisementId, String? ackValue) async {
String url = "${ApiConsts.cocRest}Mohemm_ITG_UpdateAdvertisementAsViewed"; String url = "${ApiConsts.cocRest}Mohemm_ITG_UpdateAdvertisementAsViewed";
Map<String, dynamic> postParams = <String, dynamic>{ Map<String, dynamic> postParams = {
"ItgNotificationMasterId": masterID, "ItgNotificationMasterId": masterID,
"EmployeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER.toString(), "EmployeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER.toString(),
"ItgAdvertisementId": advertisementId, "ItgAdvertisementId": advertisementId,
@ -467,14 +435,16 @@ class DashboardApiClient {
//Fetch All Courses Status //Fetch All Courses Status
Future<CoursesResponseModel?> fetchAllCoursesStatus() async { Future<CoursesResponseModel?> fetchAllCoursesStatus() async {
String url = ApiConsts.eLearningBaseUrl + "Fetch_All_Courses_Status.php"; String url = ApiConsts.eLearningBaseUrl +"Fetch_All_Courses_Status.php";
Map<String, String> headers = { Map<String, String> headers = {
'x-api-key': 'bLi@mbXJeXTHd/)h&LFh%25+%25(Nbnaq6hBg%7d%5dyQbthY%7bv6ew6-5UT\$NASwucn%7d,_PSJpuwNCCen2%7djj%7b00HR2T-%5b,k7W%7d-0yepK?%258', 'x-api-key': 'bLi@mbXJeXTHd/)h&LFh%25+%25(Nbnaq6hBg%7d%5dyQbthY%7bv6ew6-5UT\$NASwucn%7d,_PSJpuwNCCen2%7djj%7b00HR2T-%5b,k7W%7d-0yepK?%258',
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
}; };
Map<String, dynamic> postParams = {"username": AppState().memberInformationList?.eMPLOYEENUMBER ?? ""}; Map<String, dynamic> postParams = {
"username": AppState().memberInformationList?.eMPLOYEENUMBER ?? "",
};
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject(
(json) { (json) {
@ -501,32 +471,4 @@ class DashboardApiClient {
postParams, postParams,
); );
} }
Future<GenericResponseModel?> getChatBotSession() async {
String url = "${ApiConsts.erpRest}Atlas_InitiateChat";
Map<String, dynamic> postParams = {};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json);
return responseData;
},
url,
postParams,
);
}
Future<GenericResponseModel?> sendChatBotMessage({required AtlasInitiateChatResponse atlasChatTokenResponse, required String atlasChatText}) async {
String url = "${ApiConsts.erpRest}Atlas_ContinueChat";
Map<String, dynamic> postParams = <String, dynamic>{"Atlas_AccessToken": atlasChatTokenResponse.atlasAccessToken, "Atlas_ChatSessionID": atlasChatTokenResponse.atlasChatSessionId, "Atlas_ChatText": atlasChatText};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json);
return responseData;
},
url,
postParams,
);
}
} }

@ -25,23 +25,16 @@ class EITApiClient {
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject(
(json) { (json) {
// Check if GetEITTransactionList exists and is not empty
if (json['GetEITTransactionList'] == null || (json['GetEITTransactionList'] is List && json['GetEITTransactionList'].isEmpty)) {
return isCompleteList ? <List<CollectionTransaction>>[] : <CollectionTransaction>[];
}
if (isCompleteList) { if (isCompleteList) {
List<List<CollectionTransaction>> responseData = []; List<List<CollectionTransaction>> responseData = [];
json['GetEITTransactionList'].forEach((element) { json['GetEITTransactionList'].forEach((element) {
var transactionList = GetEitTransactionsModel.fromJson(element).collectionTransaction; var transactionList = GetEitTransactionsModel.fromJson(element).collectionTransaction;
if (transactionList != null && transactionList.isNotEmpty) { if (transactionList != null) responseData.add(transactionList);
responseData.add(transactionList);
}
}); });
return responseData; return responseData;
} else { } else {
List<CollectionTransaction>? responseData = GetEitTransactionsModel.fromJson(json['GetEITTransactionList'][0]).collectionTransaction; List<CollectionTransaction>? responseData = GetEitTransactionsModel.fromJson(json['GetEITTransactionList'][0]).collectionTransaction;
return responseData ?? <CollectionTransaction>[]; return responseData;
} }
}, },
url, url,

@ -1,20 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/api/api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/consts.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/models/basic_member_information_model.dart'; import 'package:mohem_flutter_app/models/basic_member_information_model.dart';
import 'package:mohem_flutter_app/models/check_mobile_app_version_model.dart'; import 'package:mohem_flutter_app/models/check_mobile_app_version_model.dart';
import 'package:mohem_flutter_app/models/generic_response_model.dart'; import 'package:mohem_flutter_app/models/generic_response_model.dart';
import 'package:mohem_flutter_app/models/dependent/get_mobile_login_info_dependent_model.dart';
import 'package:mohem_flutter_app/models/get_mobile_login_info_list_model.dart'; import 'package:mohem_flutter_app/models/get_mobile_login_info_list_model.dart';
import 'package:mohem_flutter_app/models/member_login_list_model.dart'; import 'package:mohem_flutter_app/models/member_login_list_model.dart';
import 'package:sms_otp_auto_verify/sms_otp_auto_verify.dart';
class LoginApiClient { class LoginApiClient {
static final LoginApiClient _instance = LoginApiClient._internal(); static final LoginApiClient _instance = LoginApiClient._internal();
@ -23,142 +16,33 @@ class LoginApiClient {
factory LoginApiClient() => _instance; factory LoginApiClient() => _instance;
static String? _cachedSignature; Future<GetMobileLoginInfoListModel?> getMobileLoginInfoNEW(String deviceToken, String deviceType) async {
static Future<String?> getSignature() async {
if (!Platform.isAndroid) return null;
if (_cachedSignature != null && _cachedSignature!.isNotEmpty) {
return _cachedSignature;
}
try {
String? sig = await SmsVerification.getAppSignature();
if (sig != null && sig.isNotEmpty) {
_cachedSignature = sig;
}
return sig;
} catch (e) {
debugPrint("Error getting app signature: $e");
return null;
}
}
Future<GetMobileLoginInfoListModel?> getMobileLoginInfoNEW(String deviceToken, String deviceType, {int? memberType}) async {
String url = "${ApiConsts.erpRest}Mohemm_GetMobileLoginInfo_NEW"; String url = "${ApiConsts.erpRest}Mohemm_GetMobileLoginInfo_NEW";
Map<String, dynamic> postParams = {}; Map<String, dynamic> postParams = {};
postParams["DeviceToken"] = deviceToken; postParams["DeviceToken"] = deviceToken;
postParams["DeviceType"] = deviceType; postParams["DeviceType"] = deviceType;
postParams["MemberType"] = memberType ?? ApiConsts.selectedMemberType.value;
return await ApiClient().postJsonForObject((json) { return await ApiClient().postJsonForObject((json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
if (responseData != null) {
bool? isEmp = responseData.isEmpLastLoginToDevice;
if (isEmp == null && json is Map) {
dynamic raw = json["IsEmpLastLoginToDevice"] ?? json["isEmpLastLoginToDevice"];
if (raw is bool) {
isEmp = raw;
} else if (raw is num) {
isEmp = raw == 1;
} else if (raw is String) {
isEmp = raw.toLowerCase() == "true" || raw == "1";
}
}
AppState().setIsEmpLastLoginToDevice = isEmp;
}
return (responseData.mohemmGetMobileLoginInfoList?.length ?? 0) > 0 ? (responseData.mohemmGetMobileLoginInfoList!.first) : null; return (responseData.mohemmGetMobileLoginInfoList?.length ?? 0) > 0 ? (responseData.mohemmGetMobileLoginInfoList!.first) : null;
}, url, postParams); }, url, postParams);
} }
Future<GetMobileLoginInfoDependentModel?> getMobileLoginInfoNEWDependent(String deviceToken, String deviceType, {int? memberType}) async {
String url = "${ApiConsts.erpRest}Mohemm_GetMobileLoginInfo_NEW_Dependent";
String effectiveDeviceToken = deviceToken;
if (effectiveDeviceToken.isEmpty) {
effectiveDeviceToken = AppState().getDeviceToken ?? "";
}
// if (effectiveDeviceToken.isEmpty && !AppState().getIsHuawei) {
// try {
// await Firebase.initializeApp();
// String? tok = await FirebaseMessaging.instance.getToken();
// if (tok != null && tok.isNotEmpty) {
// effectiveDeviceToken = tok;
// AppState().setDeviceToken = tok;
// }
// } catch (_) {}
// }
Map<String, dynamic> postParams = {};
postParams["DeviceToken"] = effectiveDeviceToken;
postParams["DeviceType"] = deviceType;
postParams["MemberType"] = memberType ?? MemberType.dependent.value;
return await ApiClient().postJsonForObject((json) {
if (kDebugMode) {
print("Mohemm_GetMobileLoginInfo_NEW_Dependent Response: $json");
}
List? list = json['Mohemm_GetMobileLoginInfo_NEW_DependentList'] ??
json['Mohemm_GetMobileLoginInfo_NEW_Dependent'] ??
json['Mohemm_GetMobileLoginInfo_DependentList'] ??
json['Mohemm_GetMobileLoginInfoDependentList'] ??
json['Mohemm_GetMobileLoginInfoList'] ??
json['GetMobileLoginInfoList'] ??
json['Response'] ??
json['Data'];
if (list != null && list.isNotEmpty && list.first is Map<String, dynamic>) {
return GetMobileLoginInfoDependentModel.fromJson(list.first as Map<String, dynamic>);
}
if (json is Map<String, dynamic> &&
(json['DependentID'] != null || json['DepedentName'] != null || json['DOB'] != null || json['NationalID'] != null || json['LoginType'] != null)) {
return GetMobileLoginInfoDependentModel.fromJson(json);
}
return null;
}, url, postParams);
}
Future<GenericResponseModel?> insertMobileLoginInfoNEW( Future<GenericResponseModel?> insertMobileLoginInfoNEW(
String email, int sessionId, String employeeName, int loginType, String mobileNumber, String userName, String? deviceToken, String deviceType, String email, int sessionId, String employeeName, int loginType, String mobileNumber, String userName, String deviceToken, String deviceType) async {
{int? memberType}) async {
String url = "${ApiConsts.erpRest}Mohemm_InsertMobileLoginInfo"; String url = "${ApiConsts.erpRest}Mohemm_InsertMobileLoginInfo";
String devType = deviceType.isNotEmpty ? deviceType : (Platform.isAndroid ? "android" : "ios");
String devToken = (deviceToken != null && deviceToken.isNotEmpty && deviceToken != "null")
? deviceToken
: (AppState().getIsHuawei ? AppState().getHuaweiPushToken : (AppState().getDeviceToken ?? ""));
if (devToken.isEmpty || devToken == "null") {
try {
String savedToken = await Utils.getStringFromPrefs("P_DEP_DEVICE_TOKEN");
if (savedToken.isNotEmpty && savedToken != "null") {
devToken = savedToken;
}
} catch (_) {}
}
if (devToken.isEmpty || devToken == "null") {
try {
if (!AppState().getIsHuawei) {
String? tok = await FirebaseMessaging.instance.getToken();
if (tok != null && tok.isNotEmpty) {
devToken = tok;
AppState().setDeviceToken = tok;
Utils.saveStringFromPrefs("P_DEP_DEVICE_TOKEN", tok);
}
}
} catch (_) {}
}
Map<String, dynamic> postParams = { Map<String, dynamic> postParams = {
"MobileNumber": mobileNumber, "MobileNumber": mobileNumber,
"P_USER_NAME": userName, "P_USER_NAME": userName,
"UserName": userName, "UserName": userName,
"CompanyID": 1, // todo 'sikander' @discuss umer for companyID "CompanyID": 1, // todo 'sikander' @discuss umer for companyID
"DeviceToken": devToken, "DeviceToken": deviceToken,
"LoginType": loginType, "LoginType": loginType,
"EmployeeName": employeeName, "EmployeeName": employeeName,
"P_SESSION_ID": sessionId, "P_SESSION_ID": sessionId,
"P_EMAIL_ADDRESS": email, "P_EMAIL_ADDRESS": email
"MemberType": memberType ?? ApiConsts.selectedMemberType.value,
}; };
postParams["DeviceToken"] = devToken; postParams["DeviceToken"] = deviceToken;
postParams["DeviceType"] = devType; postParams["DeviceType"] = deviceType;
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject((json) { return await ApiClient().postJsonForObject((json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
@ -167,93 +51,6 @@ class LoginApiClient {
}, url, postParams); }, url, postParams);
} }
Future<GenericResponseModel?> insertMobileLoginInfoDependent({
required int loginType,
String? nationalId,
String? dob,
String? dependentName,
String? mobileNumber,
String? logInTokenID,
String? tokenID,
int? sessionId,
String? email,
String? deviceToken,
String? deviceType,
int? companyId,
int? versionId,
int? channel,
int? memberType,
BuildContext? context,
}) async {
String url = "${ApiConsts.erpRest}Mohemm_InsertMobileLoginInfo_Dependent";
String devType = deviceType ?? (Platform.isAndroid ? "android" : "ios");
String devToken = (deviceToken != null && deviceToken.isNotEmpty && deviceToken != "null")
? deviceToken
: (AppState().getIsHuawei ? AppState().getHuaweiPushToken : (AppState().getDeviceToken ?? ""));
if (devToken.isEmpty || devToken == "null") {
try {
String savedToken = await Utils.getStringFromPrefs("P_DEP_DEVICE_TOKEN");
if (savedToken.isNotEmpty && savedToken != "null") {
devToken = savedToken;
}
} catch (_) {}
}
if (devToken.isEmpty || devToken == "null") {
try {
if (!AppState().getIsHuawei) {
String? tok = await FirebaseMessaging.instance.getToken();
if (tok != null && tok.isNotEmpty) {
devToken = tok;
AppState().setDeviceToken = tok;
Utils.saveStringFromPrefs("P_DEP_DEVICE_TOKEN", tok);
}
}
} catch (_) {}
}
String effectiveToken = (tokenID != null && tokenID.isNotEmpty)
? tokenID
: (AppState().postParamsObject?.tokenID ?? AppState().authenticationTokenID ?? "");
Map<String, dynamic> postParams = {
"VersionID": versionId ?? 5,
"Channel": channel,
"LanguageID": context != null ? AppState().getLanguageID(context) : 2,
"MemberType": memberType ?? 1,
"MobileType": devType,
"MobileNumber": mobileNumber ?? "",
"UserName": nationalId ?? "",
"P_USER_NAME": nationalId ?? "",
"P_DEP_NATIONAL_ID": nationalId ?? "",
"P_DEP_BIRTH_DATE": dob ?? "",
"P_DEPENDENT_NAME": dependentName ?? "",
"EmployeeName": dependentName ?? "",
"LogInTokenID": logInTokenID ?? (AppState().postParamsObject?.logInTokenID ?? ""),
"CompanyID": companyId ?? 1,
"DeviceType": devType,
"DeviceToken": devToken,
"LoginType": loginType,
"P_SESSION_ID": sessionId ?? (AppState().postParamsObject?.pSessionId ?? 0),
"TokenID": effectiveToken,
"P_EMAIL_ADDRESS": email ?? "",
};
if (kDebugMode) {
print("Mohemm_InsertMobileLoginInfo (Dependent) Request: $postParams");
}
return await ApiClient().postJsonForObject((json) {
if (kDebugMode) {
print("Mohemm_InsertMobileLoginInfo (Dependent) Response: $json");
}
GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
AppState().setLogged = true;
return responseData;
}, url, postParams);
}
Future<CheckMobileAppVersionModel> checkMobileAppVersion() async { Future<CheckMobileAppVersionModel> checkMobileAppVersion() async {
String url = "${ApiConsts.utilitiesRest}CheckMobileAppVersion"; String url = "${ApiConsts.utilitiesRest}CheckMobileAppVersion";
Map<String, dynamic> postParams = {}; Map<String, dynamic> postParams = {};
@ -272,31 +69,16 @@ class LoginApiClient {
}, url, postParams); }, url, postParams);
} }
Future<BasicMemberInformationModel?> mohemmSendActivationCodeByOTPNotificationType( Future<BasicMemberInformationModel?> mohemmSendActivationCodeByOTPNotificationType(int isMobileFingerPrint, String? mobileNumber, int optSendType, String? pUserName) async {
int isMobileFingerPrint,
String? mobileNumber,
int optSendType,
String? pUserName, {
String? smsSignature,
}) async {
String url = "${ApiConsts.erpRest}Mohemm_SendActivationCodebyOTPNotificationType"; String url = "${ApiConsts.erpRest}Mohemm_SendActivationCodebyOTPNotificationType";
String? sig = smsSignature ?? await getSignature(); Map<String, dynamic> postParams = {"IsMobileFingerPrint": isMobileFingerPrint, "MobileNumber": mobileNumber, "OTP_SendType": optSendType, "P_USER_NAME": pUserName};
Map<String, dynamic> postParams = {
"IsMobileFingerPrint": isMobileFingerPrint,
"MobileNumber": mobileNumber,
"OTP_SendType": optSendType,
"P_USER_NAME": pUserName,
"NotificationCenterAppGUID": ApiConsts.notificationCenterAppGUID,
"NotificationCenterAppSeviceGUID": ApiConsts.notificationCenterLoginServiceGUID,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
postParams["smsSignature"] = sig ?? "";
return await ApiClient().postJsonForObject((json) => GenericResponseModel.fromJson(json).basicMemberInformation, url, postParams); return await ApiClient().postJsonForObject((json) => GenericResponseModel.fromJson(json).basicMemberInformation, url, postParams);
} }
Future<GenericResponseModel?> checkActivationCode(bool isDeviceNFC, String? mobileNumber, String activationCode, String? pUserName, String loginType) async { Future<GenericResponseModel?> checkActivationCode(bool isDeviceNFC, String? mobileNumber, String activationCode, String? pUserName) async {
String url = "${ApiConsts.erpRest}CheckActivationCode"; String url = "${ApiConsts.erpRest}CheckActivationCode";
Map<String, dynamic> postParams = {"IsDeviceNFC": isDeviceNFC, "MobileNumber": mobileNumber, "activationCode": activationCode, "P_USER_NAME": pUserName, "LoginType":loginType}; Map<String, dynamic> postParams = {"IsDeviceNFC": isDeviceNFC, "MobileNumber": mobileNumber, "activationCode": activationCode, "P_USER_NAME": pUserName};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject((json) { return await ApiClient().postJsonForObject((json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json); GenericResponseModel responseData = GenericResponseModel.fromJson(json);
@ -308,10 +90,8 @@ class LoginApiClient {
AppState().postParamsObject?.pUserName = AppState().getUserName; AppState().postParamsObject?.pUserName = AppState().getUserName;
AppState().postParamsObject?.pSelectedEmployeeNumber = AppState().getUserName; AppState().postParamsObject?.pSelectedEmployeeNumber = AppState().getUserName;
AppState().postParamsObject?.setPLegislationCode = responseData.basicMemberInformation!.pLEGISLATIONCODE; AppState().postParamsObject?.setPLegislationCode = responseData.basicMemberInformation!.pLEGISLATIONCODE;
AppState().postParamsInitConfig.setPLegislationCode = responseData.basicMemberInformation!.pLEGISLATIONCODE;
AppState().postParamsObject?.setPayrollCodeStr = responseData.memberInformationList!.first.pAYROLLCODE; AppState().postParamsObject?.setPayrollCodeStr = responseData.memberInformationList!.first.pAYROLLCODE;
AppState().setBusinessCardPrivilege = responseData.businessCardPrivilege ?? false; AppState().setBusinessCardPrivilege = responseData.businessCardPrivilege ?? false;
AppState().setPrivilegeListModel = responseData.privilegeList ?? [];
return responseData; return responseData;
}, url, postParams); }, url, postParams);
} }
@ -323,19 +103,10 @@ class LoginApiClient {
return await ApiClient().postJsonForObject((json) => GenericResponseModel.fromJson(json).basicMemberInformation, url, postParams); return await ApiClient().postJsonForObject((json) => GenericResponseModel.fromJson(json).basicMemberInformation, url, postParams);
} }
Future<GenericResponseModel?> sendPublicActivationCode(String? mobileNumber, String? pUsername, {String? smsSignature}) async { Future<GenericResponseModel?> sendPublicActivationCode(String? mobileNumber, String? pUsername) async {
String url = "${ApiConsts.erpRest}SendPublicActivationCode"; String url = "${ApiConsts.erpRest}SendPublicActivationCode";
String? sig = smsSignature ?? await getSignature(); Map<String, dynamic> postParams = {"MobileNumber": mobileNumber, "P_MOBILE_NUMBER": mobileNumber, "P_USER_NAME": pUsername};
Map<String, dynamic> postParams = {
"MobileNumber": mobileNumber,
"P_MOBILE_NUMBER": mobileNumber,
"P_USER_NAME": pUsername,
"NotificationCenterAppGUID": ApiConsts.notificationCenterAppGUID,
"NotificationCenterAppSeviceGUID": ApiConsts.notificationCenterForgotPasswordServiceGUID,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
postParams["smsSignature"] = sig ?? "";
postParams["SMSSignature"] = sig ?? "";
return await ApiClient().postJsonForObject((json) { return await ApiClient().postJsonForObject((json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json); GenericResponseModel responseData = GenericResponseModel.fromJson(json);
AppState().postParamsObject?.setLogInTokenID = responseData.logInTokenID; AppState().postParamsObject?.setLogInTokenID = responseData.logInTokenID;
@ -385,144 +156,4 @@ class LoginApiClient {
return responseData; return responseData;
}, url, postParams); }, url, postParams);
} }
Future<GenericResponseModel?> memberLoginDependent({
required String nationalId,
required String birthDate,
String? deviceToken,
String? deviceType,
BuildContext? context,
}) async {
String url = "${ApiConsts.erpRest}MemberLogin_Dependent";
String devType = deviceType ?? (Platform.isAndroid ? "android" : "ios");
String devToken = deviceToken ?? "";
if (devToken.isEmpty) {
devToken = AppState().getIsHuawei
? AppState().getHuaweiPushToken
: (AppState().getDeviceToken ?? "");
}
if (devToken.isEmpty && !AppState().getIsHuawei) {
try {
String savedToken = await Utils.getStringFromPrefs("P_DEP_DEVICE_TOKEN");
if (savedToken.isNotEmpty) {
devToken = savedToken;
}
} catch (_) {}
}
Map<String, dynamic> postParams = {
"LanguageID": context != null ? AppState().getLanguageID(context) : 2,
"DeviceToken": devToken,
"DeviceType": devType,
"MobileType": devType,
"P_DEP_NATIONAL_ID": nationalId,
"P_DEP_BIRTH_DATE": birthDate,
};
if (kDebugMode) {
print("MemberLogin_Dependent Request: $postParams");
}
return await ApiClient().postJsonForObject((json) {
if (kDebugMode) {
print("MemberLogin_Dependent Response: $json");
}
GenericResponseModel responseData = GenericResponseModel.fromJson(json);
if (responseData.logInTokenID != null && responseData.logInTokenID!.isNotEmpty) {
AppState().postParamsObject?.setLogInTokenID = responseData.logInTokenID;
}
String? authToken = (responseData.authenticationTokenID != null && responseData.authenticationTokenID!.trim().isNotEmpty)
? responseData.authenticationTokenID!.trim()
: null;
if (authToken != null && authToken.isNotEmpty) {
AppState().postParamsObject?.setTokenID = authToken;
AppState().setAuthenticationTokenID = authToken;
}
if (responseData.getWalaplusDepInfoModel != null) {
AppState().setWalaplusDepInfoModel = responseData.getWalaplusDepInfoModel;
if (responseData.getWalaplusDepInfoModel?.pDependentName != null &&
responseData.getWalaplusDepInfoModel!.pDependentName!.trim().isNotEmpty) {
AppState().setDependentName = responseData.getWalaplusDepInfoModel!.pDependentName!.trim();
}
}
AppState().setUserName = nationalId;
AppState().postParamsObject?.setUserName = nationalId;
AppState().postParamsObject?.setPUserName = nationalId;
return responseData;
}, url, postParams);
}
Future<GenericResponseModel?> mohemmSendOTPDependent({
required String nationalId,
required String mobileNumber,
required String logInTokenID,
BuildContext? context,
String? smsSignature,
}) async {
String url = "${ApiConsts.erpRest}Mohemm_SendOTP_Dependent";
String? sig = smsSignature ?? await getSignature();
Map<String, dynamic> postParams = {
"VersionID": 20,
"LanguageID": context != null ? AppState().getLanguageID(context) : 2,
"MobileType": Platform.isAndroid ? "android" : "ios",
"OTP_SendType": 1,
"MobileNumber": mobileNumber,
"IsMobileFingerPrint": 0,
"P_DEP_NATIONAL_ID": nationalId,
"P_USER_NAME": nationalId,
"LogInTokenID": logInTokenID,
"NotificationCenterAppGUID": ApiConsts.notificationCenterAppGUID,
"NotificationCenterAppSeviceGUID": ApiConsts.notificationCenterLoginServiceGUID,
"smsSignature": (sig != null && sig.isNotEmpty) ? sig : "",
};
return await ApiClient().postJsonForObject((json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json);
return responseData;
}, url, postParams);
}
Future<GenericResponseModel?> mohemmCheckOTPDependent({
required String logInTokenID,
required String activationCode,
required String nationalId,
required String mobileNumber,
BuildContext? context,
}) async {
String url = "${ApiConsts.erpRest}Mohemm_CheckOTP_Dependent";
Map<String, dynamic> postParams = {
"LogInTokenID": logInTokenID,
"activationCode": activationCode,
"P_USER_NAME": nationalId,
"P_DEP_NATIONAL_ID": nationalId,
"MobileNumber": mobileNumber,
"LanguageID": context != null ? AppState().getLanguageID(context) : 2,
"MobileType": Platform.isAndroid ? "android" : "ios",
};
return await ApiClient().postJsonForObject((json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json);
String effectiveToken = (responseData.authenticationTokenID != null && responseData.authenticationTokenID!.trim().isNotEmpty)
? responseData.authenticationTokenID!.trim()
: (AppState().postParamsObject?.tokenID ?? AppState().authenticationTokenID ?? "");
AppState().postParamsObject?.setTokenID = effectiveToken;
AppState().setAuthenticationTokenID = effectiveToken;
AppState().postParamsObject?.setLogInTokenID = (responseData.logInTokenID != null && responseData.logInTokenID!.isNotEmpty)
? responseData.logInTokenID!
: logInTokenID;
AppState().postParamsObject?.setUserName = nationalId;
AppState().postParamsObject?.setPUserName = nationalId;
AppState().postParamsObject?.setMobileNumer = mobileNumber;
AppState().setUserName = nationalId;
AppState().isAuthenticated = true;
AppState().setLogged = true;
if (responseData.getWalaplusDepInfoModel != null) {
AppState().setWalaplusDepInfoModel = responseData.getWalaplusDepInfoModel;
if (responseData.getWalaplusDepInfoModel?.pDependentName != null &&
responseData.getWalaplusDepInfoModel!.pDependentName!.trim().isNotEmpty) {
AppState().setDependentName = responseData.getWalaplusDepInfoModel!.pDependentName!.trim();
}
}
return responseData;
}, url, postParams);
}
} }

@ -104,18 +104,24 @@ class MarathonApiClient {
} }
Future<QuestionModel> getNextQuestion({required String? questionId, required String marathonId}) async { Future<QuestionModel> getNextQuestion({required String? questionId, required String marathonId}) async {
Map<String, String?> jsonObject = <String, String?>{"previousQuestionId": questionId, "marathonId": marathonId}; Map<String, String?> jsonObject = <String, String?>{
"previousQuestionId": questionId,
"marathonId": marathonId,
};
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject(
(json) { (json) {
MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json); MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);
if (marathonModel.statusCode == 404 || marathonModel.statusCode == 208 || marathonModel.statusCode == 204 || marathonModel.statusCode == 500) { if (marathonModel.statusCode == 404 || marathonModel.statusCode == 208 || marathonModel.statusCode == 204 || marathonModel.statusCode == 500) {
Utils.showToast( Utils.confirmDialog(
AppRoutes.navigatorKey.currentContext,
marathonModel.message!, marathonModel.message!,
onComplete: () { onTap: () {
AppRoutes.navigatorKey.currentContext!.read<MarathonProvider>().resetValues(); AppRoutes.navigatorKey.currentContext!.read<MarathonProvider>().resetValues();
Navigator.of(AppRoutes.navigatorKey.currentContext!).popUntil(ModalRoute.withName(AppRoutes.dashboard)); Navigator.of(
AppRoutes.navigatorKey.currentContext!,
).popUntil(ModalRoute.withName(AppRoutes.dashboard));
}, },
); );
} }
@ -208,99 +214,99 @@ class MarathonApiClient {
return null; return null;
} }
// late HubConnection hubConnection; // late HubConnection hubConnection;
// Future<void> buildHubConnection(BuildContext context, String prizeId) async { // Future<void> buildHubConnection(BuildContext context, String prizeId) async {
// HttpConnectionOptions httpOptions = HttpConnectionOptions(skipNegotiation: false, logMessageContent: true); // HttpConnectionOptions httpOptions = HttpConnectionOptions(skipNegotiation: false, logMessageContent: true);
// hubConnection = HubConnectionBuilder() // hubConnection = HubConnectionBuilder()
// .withUrl( // .withUrl(
// ApiConsts.marathonHubConnectionUrl + "?employeeNumber=${AppState().memberInformationList!.eMPLOYEENUMBER ?? ""}&employeeName=${AppState().memberInformationList!.eMPLOYEENAME ?? ""}", // ApiConsts.marathonHubConnectionUrl + "?employeeNumber=${AppState().memberInformationList!.eMPLOYEENUMBER ?? ""}&employeeName=${AppState().memberInformationList!.eMPLOYEENAME ?? ""}",
// options: httpOptions, // options: httpOptions,
// ) // )
// .withAutomaticReconnect( // .withAutomaticReconnect(
// retryDelays: <int>[2000, 5000, 10000, 20000], // retryDelays: <int>[2000, 5000, 10000, 20000],
// ) // )
// .configureLogging( // .configureLogging(
// Logger("Logging"), // Logger("Logging"),
// ) // )
// .build(); // .build();
// hubConnection.onclose( // hubConnection.onclose(
// ({Exception? error}) { // ({Exception? error}) {
// logger.i("onclose"); // logger.i("onclose");
// }, // },
// ); // );
// hubConnection.onreconnecting( // hubConnection.onreconnecting(
// ({Exception? error}) { // ({Exception? error}) {
// logger.i("onreconnecting"); // logger.i("onreconnecting");
// }, // },
// ); // );
// hubConnection.onreconnected( // hubConnection.onreconnected(
// ({String? connectionId}) { // ({String? connectionId}) {
// logger.i("onreconnected"); // logger.i("onreconnected");
// }, // },
// ); // );
// if (hubConnection.state != HubConnectionState.Connected) { // if (hubConnection.state != HubConnectionState.Connected) {
// await hubConnection.start(); // await hubConnection.start();
// logger.i("Started HubConnection"); // logger.i("Started HubConnection");
// //
// await hubConnection.invoke( // await hubConnection.invoke(
// "AddParticipant", // "AddParticipant",
// args: <Object>[ // args: <Object>[
// <String, dynamic>{ // <String, dynamic>{
// "employeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER ?? "", // "employeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER ?? "",
// "employeeName": AppState().memberInformationList!.eMPLOYEENAME ?? "", // "employeeName": AppState().memberInformationList!.eMPLOYEENAME ?? "",
// "marathonId": AppState().getMarathonProjectId, // "marathonId": AppState().getMarathonProjectId,
// "prizeId": "8577B2E8-5DD7-43F0-10DD-08DACB0AC064", // "prizeId": "8577B2E8-5DD7-43F0-10DD-08DACB0AC064",
// } // }
// ], // ],
// ).catchError((e) { // ).catchError((e) {
// logger.i("Error in AddParticipant: $e"); // logger.i("Error in AddParticipant: $e");
// }); // });
// //
// context.read<MarathonProvider>().addItemToList(ApiConsts.dummyQuestion); // context.read<MarathonProvider>().addItemToList(ApiConsts.dummyQuestion);
// //
// await hubConnection.invoke( // await hubConnection.invoke(
// "SendQuestionToParticipant", // "SendQuestionToParticipant",
// args: <Object>[ // args: <Object>[
// <String, dynamic>{ // <String, dynamic>{
// "marathonId": "${AppState().getMarathonProjectId}", // "marathonId": "${AppState().getMarathonProjectId}",
// } // }
// ], // ],
// ).catchError((e) { // ).catchError((e) {
// Utils.confirmDialog(context, e.toString()); // Utils.confirmDialog(context, e.toString());
// logger.i("Error in SendQuestionToParticipant: $e"); // logger.i("Error in SendQuestionToParticipant: $e");
// }); // });
// //
// try { // try {
// hubConnection.on("OnSendQuestionToParticipant", (List<Object?>? arguments) { // hubConnection.on("OnSendQuestionToParticipant", (List<Object?>? arguments) {
// onSendQuestionToParticipant(arguments, context); // onSendQuestionToParticipant(arguments, context);
// }); // });
// } catch (e, s) { // } catch (e, s) {
// logger.i("Error in OnSendQuestionToParticipant"); // logger.i("Error in OnSendQuestionToParticipant");
// } // }
// //
// try { // try {
// hubConnection.on("OnParticipantJoin", (List<Object?>? arguments) { // hubConnection.on("OnParticipantJoin", (List<Object?>? arguments) {
// onParticipantJoin(arguments, context); // onParticipantJoin(arguments, context);
// }); // });
// } catch (e, s) { // } catch (e, s) {
// logger.i("Error in OnParticipantJoin"); // logger.i("Error in OnParticipantJoin");
// } // }
// } // }
// } // }
// //
// Future<void> onSendQuestionToParticipant(List<Object?>? arguments, BuildContext context) async { // Future<void> onSendQuestionToParticipant(List<Object?>? arguments, BuildContext context) async {
// logger.i("onSendQuestionToParticipant arguments: $arguments"); // logger.i("onSendQuestionToParticipant arguments: $arguments");
// //
// if (arguments != null) { // if (arguments != null) {
// Map<dynamic, dynamic> data = arguments.first! as Map<dynamic, dynamic>; // Map<dynamic, dynamic> data = arguments.first! as Map<dynamic, dynamic>;
// var json = data["data"]; // var json = data["data"];
// QuestionModel newQuestion = QuestionModel.fromJson(json); // QuestionModel newQuestion = QuestionModel.fromJson(json);
// AppRoutes.navigatorKey.currentContext!.read<MarathonProvider>().onNewQuestionReceived(newQuestion); // AppRoutes.navigatorKey.currentContext!.read<MarathonProvider>().onNewQuestionReceived(newQuestion);
// } // }
// } // }
// //
// Future<void> onParticipantJoin(List<Object?>? arguments, BuildContext context) async { // Future<void> onParticipantJoin(List<Object?>? arguments, BuildContext context) async {
// logger.i("OnParticipantJoin arguments: $arguments"); // logger.i("OnParticipantJoin arguments: $arguments");
// context.watch<MarathonProvider>().totalMarathoners++; // context.watch<MarathonProvider>().totalMarathoners++;
// } // }
} }

@ -55,9 +55,9 @@ class MyAttendanceApiClient {
}, url, postParams); }, url, postParams);
} }
Future<GenericResponseModel> getCEIDffStructure(String pFunctionName, String? empID, int? relationID) async { Future<GenericResponseModel> getCEIDffStructure(String pFunctionName, String? empID) async {
String url = "${ApiConsts.erpRest}GET_CEI_DFF_STRUCTURE"; String url = "${ApiConsts.erpRest}GET_CEI_DFF_STRUCTURE";
Map<String, dynamic> postParams = {"P_FUNCTION_NAME": pFunctionName,"P_CONTACT_RELATIONSHIP_ID": relationID}; Map<String, dynamic> postParams = {"P_FUNCTION_NAME": pFunctionName};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
if (empID != null) postParams["P_SELECTED_EMPLOYEE_NUMBER"] = empID; if (empID != null) postParams["P_SELECTED_EMPLOYEE_NUMBER"] = empID;
return await ApiClient().postJsonForObject((json) { return await ApiClient().postJsonForObject((json) {

@ -1,24 +1,13 @@
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/api/api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/consts.dart';
import 'package:mohem_flutter_app/models/generic_response_model.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/get_categories_list.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/get_categories_list.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_category_model.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_city_model.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_retailer_model.dart';
import 'package:mohem_flutter_app/widgets/location/Location.dart' as location;
class OffersAndDiscountsApiClient { class OffersAndDiscountsApiClient {
static final OffersAndDiscountsApiClient _instance = static final OffersAndDiscountsApiClient _instance = OffersAndDiscountsApiClient._internal();
OffersAndDiscountsApiClient._internal();
static List<WalaPlusCategoryModel>? cachedCategories;
OffersAndDiscountsApiClient._internal(); OffersAndDiscountsApiClient._internal();
@ -28,12 +17,7 @@ class OffersAndDiscountsApiClient {
List<GetCategoriesList> getSaleCategoriesList = []; List<GetCategoriesList> getSaleCategoriesList = [];
String url = "${ApiConsts.cocRest}Mohemm_ITG_GetCategories"; String url = "${ApiConsts.cocRest}Mohemm_ITG_GetCategories";
Map<String, dynamic> postParams = { Map<String, dynamic> postParams = {"EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER, "ItgPageSize": 100, "ItgPageNo": 1, "ItgIsActive": true};
"EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER,
"ItgPageSize": 100,
"ItgPageNo": 1,
"ItgIsActive": true,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject(
@ -60,19 +44,11 @@ class OffersAndDiscountsApiClient {
); );
} }
Future<List<OffersListModel>> getOffersList( Future<List<OffersListModel>> getOffersList(int categoryID, int pageSize) async {
int categoryID,
int pageSize,
) async {
List<OffersListModel> getSaleCategoriesList = []; List<OffersListModel> getSaleCategoriesList = [];
String url = "${ApiConsts.cocRest}GetOfferDiscountsConfigData"; String url = "${ApiConsts.cocRest}GetOfferDiscountsConfigData";
Map<String, dynamic> postParams = { Map<String, dynamic> postParams = {"EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER, "ItgPageSize": pageSize, "ItgPageNo": 1, "ItgCategoryID": categoryID};
"EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER,
"ItgPageSize": pageSize,
"ItgPageNo": 1,
"ItgCategoryID": categoryID,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject(
@ -92,918 +68,4 @@ class OffersAndDiscountsApiClient {
postParams, postParams,
); );
} }
String _resolveUserName() {
String? pUserName = AppState().getUserName;
if (pUserName == null || pUserName.isEmpty) {
pUserName =
AppState().postParamsObject?.userName ??
AppState().postParamsObject?.pUserName ??
AppState().memberInformationList?.eMPLOYEENUMBER;
}
return pUserName ?? "";
}
// Same Location.isEnabled -> Location.havePermission -> Location.getCurrentLocation pattern
// used by mark_attendance_widget.dart (e.g. Wifi attendance), wrapped as a Future since this
// is a background enrichment of the request, not a blocking user action - any failure (service
// disabled, permission denied, error) just resolves to null so the retailer fetch still proceeds
// without WP_Geo_Location rather than showing permission dialogs here.
Future<Position?> _getCurrentPositionSilently(BuildContext context) async {
Completer<bool> enabledCompleter = Completer<bool>();
location.Location.isEnabled((bool isEnabled) {
if (!enabledCompleter.isCompleted) enabledCompleter.complete(isEnabled);
});
bool isEnabled = await enabledCompleter.future;
debugPrint(
"_getCurrentPositionSilently: location service isEnabled=$isEnabled",
);
if (!isEnabled) return null;
Completer<bool> permissionCompleter = Completer<bool>();
location.Location.havePermission((bool hasPermission) {
if (!permissionCompleter.isCompleted)
permissionCompleter.complete(hasPermission);
});
bool hasPermission = await permissionCompleter.future;
debugPrint("_getCurrentPositionSilently: havePermission=$hasPermission");
if (!hasPermission) return null;
Completer<Position?> positionCompleter = Completer<Position?>();
location.Location.getCurrentLocation(
(Position position, bool isMocked) {
debugPrint(
"_getCurrentPositionSilently: got position lat=${position.latitude}, lng=${position.longitude}, isMocked=$isMocked",
);
if (!positionCompleter.isCompleted)
positionCompleter.complete(position);
},
() {
debugPrint(
"_getCurrentPositionSilently: getCurrentLocation error callback fired",
);
if (!positionCompleter.isCompleted) positionCompleter.complete(null);
},
context,
);
return positionCompleter.future;
}
String _resolveDepNationalId() {
if (!ApiConsts.isDependetLogin) {
return "";
}
return AppState().getUserName ?? "";
}
String _resolveMobileNumber({String? overrideMobile}) {
if (overrideMobile != null && overrideMobile.trim().isNotEmpty) {
return overrideMobile.trim();
}
// postParamsObject.mobileNumber/memberInformationList/memberLoginList are only ever set by
// the employee login flow and never cleared on logout - for a dependent session, the only
// legitimate source is the sponsor's mobile captured at dependent login (walaplusDepInfoModel),
// same reasoning as _resolveUserReference above.
if (ApiConsts.isDependetLogin) {
String? depMobile = AppState().walaplusDepInfoModel?.pEmpMobileNumber;
if (depMobile != null && depMobile.trim().isNotEmpty) {
return depMobile.trim();
}
return "";
}
String? mobile = AppState().postParamsObject?.mobileNumber;
if (mobile != null && mobile.trim().isNotEmpty) {
return mobile.trim();
}
mobile = AppState().memberInformationList?.eMPLOYEEMOBILENUMBER;
if (mobile != null && mobile.trim().isNotEmpty) {
return mobile.trim();
}
mobile = AppState().walaplusDepInfoModel?.pEmpMobileNumber;
if (mobile != null && mobile.trim().isNotEmpty) {
return mobile.trim();
}
mobile = AppState().memberLoginList?.pMOBILENUMBER;
if (mobile != null && mobile.trim().isNotEmpty) {
return mobile.trim();
}
mobile = AppState().memberInformationList?.mobileNumberWithZipCode;
if (mobile != null && mobile.trim().isNotEmpty) {
return mobile.trim();
}
return "";
}
Map<String, dynamic> _getBaseWalaPlusParams() {
Map<String, dynamic> base = {};
base.addAll(AppState().postParamsJson);
if (ApiConsts.isDependetLogin) {
String? token =
AppState().postParamsObject?.tokenID ??
AppState().authenticationTokenID;
if (token != null && token.isNotEmpty) {
base["TokenID"] = token;
}
String? logInToken = AppState().postParamsObject?.logInTokenID;
if (logInToken != null && logInToken.isNotEmpty) {
base["LogInTokenID"] = logInToken;
}
}
_cleanWalaPlusParams(base);
return base;
}
void _cleanWalaPlusParams(Map<String, dynamic> map) {
map.remove("P_EMAIL_ADDRESS");
map.remove("P_SESSION_ID");
map.remove("PayrollCodeStr");
map.remove("LegislationCodeStr");
map.remove("P_SELECTED_EMPLOYEE_NUMBER");
map.removeWhere((key, value) => value == null);
}
// Pass parentId to fetch the subcategories under a given main category (WP_ParentId) -
// omit it (the default) to fetch the top-level main categories, same as before.
Future<List<WalaPlusCategoryModel>> getWalaPlusCategories({
BuildContext? context,
String? mobileNumber,
String? parentId,
}) async {
String url = ApiConsts.walaPlusCategories;
String pUserName = _resolveUserName();
String pMobileNumber = _resolveMobileNumber(overrideMobile: mobileNumber);
String pDepNationalId = _resolveDepNationalId();
Map<String, dynamic> postParams = _getBaseWalaPlusParams();
postParams["UserName"] = pUserName;
postParams["P_USER_NAME"] = pUserName;
postParams["P_DEP_NATIONAL_ID"] = pDepNationalId;
if (pMobileNumber.isNotEmpty) {
postParams["MobileNumber"] = pMobileNumber;
}
postParams["MemberType"] = ApiConsts.isDependetLogin ? 2 : 1;
postParams["CompanyID"] = 1;
if (parentId != null && parentId.trim().isNotEmpty) {
postParams["WP_Parent_Id"] = parentId.trim();
}
_cleanWalaPlusParams(postParams);
List<WalaPlusCategoryModel> list = await ApiClient().postJsonForObject(
(response) {
List<WalaPlusCategoryModel> categories = [];
if (response != null) {
dynamic cats =
response['WalaPlus_Categories'] ??
response['walaPlus_Categories'] ??
response['WalaPlusCategories'] ??
response['result'];
if (cats is String) {
try {
cats = json.decode(cats);
} catch (e) {
debugPrint("Error decoding WalaPlus_Categories: $e");
}
}
if (cats is List) {
cats.forEach((v) {
if (v is Map<String, dynamic>) {
categories.add(WalaPlusCategoryModel.fromJson(v));
} else if (v is Map) {
categories.add(
WalaPlusCategoryModel.fromJson(Map<String, dynamic>.from(v)),
);
}
});
}
}
return categories;
},
url,
postParams,
);
if ((parentId == null || parentId.trim().isEmpty) && list.isNotEmpty) {
cachedCategories = list;
}
return list;
}
// Fetched fresh every time the city picker opens (no caching) - see
// mazaya_city_selector.dart. Only P_USER_NAME/MobileNumber (resolved the same way as every
// other WalaPlus call) and WP_Country vary per call; everything else is the shared default
// WalaPlus params/token from _getBaseWalaPlusParams.
Future<List<WalaPlusCityModel>> getWalaPlusCities({
String? mobileNumber,
String? country,
}) async {
String url = ApiConsts.walaPlusCities;
String pUserName = _resolveUserName();
String pMobileNumber = _resolveMobileNumber(overrideMobile: mobileNumber);
Map<String, dynamic> postParams = _getBaseWalaPlusParams();
postParams["P_USER_NAME"] = pUserName;
if (pMobileNumber.isNotEmpty) {
postParams["MobileNumber"] = pMobileNumber;
}
postParams["WP_Country"] = country ?? "";
_cleanWalaPlusParams(postParams);
return await ApiClient().postJsonForObject(
(response) {
List<WalaPlusCityModel> cities = [];
if (response != null) {
dynamic data =
response['WalaPlus_Cities'] ?? response['walaPlus_Cities'];
if (data is String) {
try {
data = json.decode(data);
} catch (e) {
debugPrint("Error decoding WalaPlus_Cities: $e");
}
}
if (data is List) {
for (var v in data) {
if (v is Map<String, dynamic>) {
cities.add(WalaPlusCityModel.fromJson(v));
} else if (v is Map) {
cities.add(
WalaPlusCityModel.fromJson(Map<String, dynamic>.from(v)),
);
}
}
}
}
return cities;
},
url,
postParams,
);
}
// Confirmed with backend team: WP_Offer_Type accepts "online" and "store".
static const String walaPlusOfferTypeOnline = "online";
static const String walaPlusOfferTypeInStore = "store";
// Maps the screen's internal filter key ("online"/"instore"/"all") to the WP_Offer_Type value
// the backend expects. Returns null for "all" (or anything unrecognized) so callers can skip
// sending WP_Offer_Type entirely.
static String? mapOfferTypeFilter(String internalKey) {
switch (internalKey) {
case "online":
return walaPlusOfferTypeOnline;
case "instore":
return walaPlusOfferTypeInStore;
default:
return null;
}
}
Future<WalaPlusRetailersResponse?> getWalaPlusRetailers({
String? categoryId,
// Every existing caller passes categoryId (even "" for "all") and relies on WP_CategoryID
// always being present in the request - default true preserves that for everyone. Pass
// false only when the category filter must be fully omitted (e.g. a cross-category newest
// feed), not just set to empty.
bool includeCategoryId = true,
int page = 1,
int perPage = 10,
String? geoLocation,
String? offerType,
String? sortBy,
int? radius,
int? minDiscount,
// Confirmed with backend team: WP_City_ID=3 or WP_City_ID=0 both mean "all cities" (no
// filter) - any other id filters to that specific city. Default null -> 0, same "always
// send a concrete int" contract as WP_Radius/WP_Min_Discount below.
int? cityId,
BuildContext? context,
String? mobileNumber,
String? query,
String? userReference,
}) async {
String url = ApiConsts.walaPlusRetailers;
String pUserName = _resolveUserName();
String pMobileNumber = _resolveMobileNumber(overrideMobile: mobileNumber);
String pDepNationalId = _resolveDepNationalId();
String effectiveCatId =
(categoryId == "all" || categoryId == "0" || categoryId == null)
? ""
: categoryId;
Map<String, dynamic> postParams = _getBaseWalaPlusParams();
postParams["UserName"] = pUserName;
postParams["P_USER_NAME"] = pUserName;
postParams["P_DEP_NATIONAL_ID"] = pDepNationalId;
if (pMobileNumber.isNotEmpty) {
postParams["MobileNumber"] = pMobileNumber;
}
postParams["MemberType"] = ApiConsts.isDependetLogin ? 2 : 1;
postParams["CompanyID"] = 1;
if (includeCategoryId) {
postParams["WP_CategoryID"] = effectiveCatId;
}
if (offerType != null && offerType.trim().isNotEmpty) {
postParams["WP_Offer_Type"] = offerType.trim();
}
postParams["WP_Page"] = page;
postParams["WP_Per_page"] = perPage;
if (query != null && query.trim().isNotEmpty) {
postParams["WP_Query"] = query.trim();
}
if (sortBy != null && sortBy.trim().isNotEmpty) {
postParams["WP_Sort_By"] = sortBy.trim();
}
// Backend contract declares these as non-nullable int DataMembers, so "Any"/"All" (null
// here) must be sent as a concrete 0, not omitted.
postParams["WP_Radius"] = radius ?? 0;
postParams["WP_Min_Discount"] = minDiscount ?? 0;
postParams["WP_City_ID"] = cityId ?? 0;
if (geoLocation != null && geoLocation.isNotEmpty) {
postParams["WP_Geo_Location"] = geoLocation;
} else if (context != null) {
try {
Position? position = await _getCurrentPositionSilently(context);
if (position != null) {
postParams["WP_Geo_Location"] =
"${position.latitude},${position.longitude}";
}
} catch (e) {
debugPrint("getWalaPlusRetailers: unable to get current location: $e");
}
}
_cleanWalaPlusParams(postParams);
// Pass WP_User_Reference: if filled then user ID, else null value
if (userReference != null && userReference.trim().isNotEmpty) {
postParams["WP_User_Reference"] = userReference.trim();
} else {
postParams["WP_User_Reference"] = null;
}
return await ApiClient().postJsonForObject(
(response) {
if (response != null) {
dynamic val =
response['WalaPlus_Retailers'] ??
response['walaPlus_Retailers'] ??
response['WalaPlusRetailers'] ??
response['WP_Retailers'] ??
response['result'];
if (val is String) {
try {
val = json.decode(val);
} catch (e) {
debugPrint("Error decoding WalaPlus_Retailers: $e");
}
}
if (val is Map<String, dynamic>) {
return WalaPlusRetailersResponse.fromJson(val);
} else if (val is Map) {
return WalaPlusRetailersResponse.fromJson(
Map<String, dynamic>.from(val),
);
} else if (val is List) {
List<WalaPlusRetailerModel> retailers = [];
for (var v in val) {
if (v is Map<String, dynamic>) {
retailers.add(WalaPlusRetailerModel.fromJson(v));
} else if (v is Map) {
retailers.add(
WalaPlusRetailerModel.fromJson(Map<String, dynamic>.from(v)),
);
}
}
return WalaPlusRetailersResponse(
retailers: retailers,
page: 1,
pagesCount: 1,
perPage: retailers.length,
);
}
}
return null;
},
url,
postParams,
);
}
Future<WalaPlusRetailerModel?> getWalaPlusRetailerById({
required String retailerId,
BuildContext? context,
String? mobileNumber,
String? userReference,
}) async {
String url = ApiConsts.walaPlusRetailersByID;
String pUserName = _resolveUserName();
String pMobileNumber = _resolveMobileNumber(overrideMobile: mobileNumber);
String pDepNationalId = _resolveDepNationalId();
String pUserReference = _resolveUserReference(
overrideUserReference: userReference,
);
Map<String, dynamic> postParams = _getBaseWalaPlusParams();
postParams["WP_RetailerID"] = retailerId;
postParams["RetailerID"] = retailerId;
postParams["UserName"] = pUserName;
postParams["P_USER_NAME"] = pUserName;
postParams["P_DEP_NATIONAL_ID"] = pDepNationalId;
if (pMobileNumber.isNotEmpty) {
postParams["MobileNumber"] = pMobileNumber;
}
postParams["MemberType"] = ApiConsts.isDependetLogin ? 2 : 1;
postParams["CompanyID"] = 1;
if (context != null) {
postParams["LanguageID"] = AppState().getLanguageID(context);
try {
Position? position = await _getCurrentPositionSilently(context);
log("context position: $position");
if (position != null) {
postParams["WP_Geo_Location"] =
"${position.latitude},${position.longitude}";
}
} catch (e) {
debugPrint(
"getWalaPlusRetailerById: unable to get current location: $e",
);
}
}
_cleanWalaPlusParams(postParams);
// Pass WP_User_Reference: if filled then user ID, else null value
if (pUserReference.isNotEmpty) {
postParams["WP_User_Reference"] = pUserReference;
} else {
postParams["WP_User_Reference"] = null;
}
debugPrint(
"getWalaPlusRetailerById: calling $url with params: $postParams",
);
return await ApiClient().postJsonForObject(
(response) {
if (response != null) {
debugPrint(
"getWalaPlusRetailerById response keys: ${response is Map ? response.keys.toList() : 'non-map'}",
);
dynamic val =
response['WalaPlus_RetailersByID'] ??
response['walaPlus_RetailersByID'] ??
response['WalaPlusRetailersByID'] ??
response['WP_RetailersByID'] ??
response['result'];
if (val is String) {
try {
val = json.decode(val);
} catch (e) {
debugPrint(
"Error decoding WalaPlus_RetailersByID JSON string: $e",
);
}
}
WalaPlusRetailerModel? model;
if (val is List && val.isNotEmpty) {
var item = val.first;
if (item is Map<String, dynamic>) {
model = WalaPlusRetailerModel.fromJson(item);
} else if (item is Map) {
model = WalaPlusRetailerModel.fromJson(
Map<String, dynamic>.from(item),
);
}
} else if (val is Map) {
Map<String, dynamic> map =
(val is Map<String, dynamic>)
? val
: Map<String, dynamic>.from(val);
if (map.containsKey('retailer')) {
var byIdResponse = WalaPlusRetailersByIDResponse.fromJson(map);
model =
byIdResponse.primaryRetailer ??
WalaPlusRetailerModel.fromJson(map);
} else {
model = WalaPlusRetailerModel.fromJson(map);
}
if (model?.isFavorite == null) {
if (map.containsKey('isfavorite')) {
model?.isFavorite =
map['isfavorite'] == true ||
map['isfavorite'] == 1 ||
map['isfavorite'] == 'true' ||
map['isfavorite'] == '1';
} else if (map.containsKey('is_favorite')) {
model?.isFavorite =
map['is_favorite'] == true ||
map['is_favorite'] == 1 ||
map['is_favorite'] == 'true' ||
map['is_favorite'] == '1';
}
}
if ((model?.offers == null || model!.offers!.isEmpty) &&
(map.containsKey('offers') || map.containsKey('Offers'))) {
model?.offers = WalaPlusRetailerModel.parseOffersList(
map['offers'] ?? map['Offers'],
);
}
if ((model?.locations == null || model!.locations!.isEmpty) &&
(map.containsKey('locations') ||
map.containsKey('Locations') ||
map.containsKey('location') ||
map.containsKey('Location'))) {
model?.locations = WalaPlusRetailerModel.parseLocationsList(
map['locations'] ??
map['Locations'] ??
map['location'] ??
map['Location'],
);
}
if ((model?.website == null ||
model!.website!.isEmpty ||
model!.website == "null")) {
String? web =
(map['website'] ??
map['merchant_website'] ??
map['website_url'] ??
map['url'] ??
map['merchant_url'])
?.toString();
if (web != null && web.trim().isNotEmpty && web != "null") {
model?.website = web.trim();
}
}
if ((model?.categories == null || model!.categories!.isEmpty) &&
(map.containsKey('categories') ||
map.containsKey('Categories') ||
map.containsKey('category') ||
map.containsKey('Category'))) {
dynamic catData =
map['categories'] ??
map['Categories'] ??
map['category'] ??
map['Category'];
if (catData is List) {
model?.categories =
catData
.map(
(c) =>
c is Map
? WalaPlusCategoryModel.fromJson(
Map<String, dynamic>.from(c),
)
: WalaPlusCategoryModel(name: c.toString()),
)
.toList();
} else if (catData is Map) {
model?.categories = [
WalaPlusCategoryModel.fromJson(
Map<String, dynamic>.from(catData),
),
];
} else if (catData is String && catData.trim().isNotEmpty) {
model?.categories = [
WalaPlusCategoryModel(name: catData.trim()),
];
}
}
} else if (response is Map<String, dynamic> &&
response.containsKey('name')) {
model = WalaPlusRetailerModel.fromJson(response);
}
if (model != null) {
if (response is Map) {
if (model.isFavorite == null) {
if (response.containsKey('isfavorite')) {
model.isFavorite =
response['isfavorite'] == true ||
response['isfavorite'] == 1 ||
response['isfavorite'] == 'true' ||
response['isfavorite'] == '1';
} else if (response.containsKey('is_favorite')) {
model.isFavorite =
response['is_favorite'] == true ||
response['is_favorite'] == 1 ||
response['is_favorite'] == 'true' ||
response['is_favorite'] == '1';
}
}
if (model.website == null ||
model.website!.isEmpty ||
model.website == "null") {
String? web =
(response['website'] ??
response['merchant_website'] ??
response['website_url'] ??
response['url'] ??
response['merchant_url'])
?.toString();
if (web != null && web.trim().isNotEmpty && web != "null") {
model.website = web.trim();
}
}
if ((model.offers == null || model.offers!.isEmpty) &&
(response.containsKey('offers') ||
response.containsKey('Offers'))) {
model.offers = WalaPlusRetailerModel.parseOffersList(
response['offers'] ?? response['Offers'],
);
}
if ((model.locations == null || model.locations!.isEmpty) &&
(response.containsKey('locations') ||
response.containsKey('Locations') ||
response.containsKey('location') ||
response.containsKey('Location'))) {
model.locations = WalaPlusRetailerModel.parseLocationsList(
response['locations'] ??
response['Locations'] ??
response['location'] ??
response['Location'],
);
}
if ((model.categories == null || model.categories!.isEmpty) &&
(response.containsKey('categories') ||
response.containsKey('Categories') ||
response.containsKey('category') ||
response.containsKey('Category'))) {
dynamic catData =
response['categories'] ??
response['Categories'] ??
response['category'] ??
response['Category'];
if (catData is List) {
model.categories =
catData
.map(
(c) =>
c is Map
? WalaPlusCategoryModel.fromJson(
Map<String, dynamic>.from(c),
)
: WalaPlusCategoryModel(name: c.toString()),
)
.toList();
} else if (catData is Map) {
model.categories = [
WalaPlusCategoryModel.fromJson(
Map<String, dynamic>.from(catData),
),
];
} else if (catData is String && catData.trim().isNotEmpty) {
model.categories = [
WalaPlusCategoryModel(name: catData.trim()),
];
}
}
}
debugPrint(
"getWalaPlusRetailerById: parsed retailer '${model.name}', isFavorite: ${model.isFavorite}, offers: ${model.offers?.length ?? 0}, locations: ${model.locations?.length ?? 0}",
);
return model;
}
}
return null;
},
url,
postParams,
);
}
String resolveUserReference({String? overrideUserReference}) {
return _resolveUserReference(overrideUserReference: overrideUserReference);
}
String _resolveUserReference({String? overrideUserReference}) {
if (overrideUserReference != null &&
overrideUserReference.trim().isNotEmpty) {
return overrideUserReference.trim();
}
// AppState().memberInformationList is never cleared on logout, so after logging out of an
// employee session and logging back in as a dependent (without a full app restart) it can
// still be sitting there from the previous session. Checking the CURRENT session type first
// - rather than just whichever field happens to be non-empty - prevents a dependent session
// from ever resolving to the previous employee's number.
if (ApiConsts.isDependetLogin) {
String? depRef = AppState().getUserName;
if (depRef != null && depRef.trim().isNotEmpty) {
return depRef.trim();
}
return "";
}
String? ref = AppState().memberInformationList?.eMPLOYEENUMBER?.toString();
if (ref != null && ref.trim().isNotEmpty) {
return ref.trim();
}
String? pUserName = AppState().getUserName;
if (pUserName != null && pUserName.trim().isNotEmpty) {
return pUserName.trim();
}
return "";
}
Future<Map<String, dynamic>?> redeemWalaPlusOffer({
required String offerId,
String? offerType,
String? userReference,
BuildContext? context,
}) async {
String url = ApiConsts.walaPlusOfferRedeem;
String pUserName = _resolveUserName();
String pUserReference = _resolveUserReference(
overrideUserReference: userReference,
);
Map<String, dynamic> postParams = _getBaseWalaPlusParams();
postParams["WP_User_Reference"] = pUserReference;
postParams["WP_Offer_Id"] = offerId;
postParams["WP_Offer_Type"] =
(offerType != null && offerType.trim().isNotEmpty)
? offerType.trim()
: "promo";
postParams["UserName"] = pUserName;
postParams["P_USER_NAME"] = pUserName;
if (ApiConsts.isDependetLogin) {
String pDepNationalId = _resolveDepNationalId();
if (pDepNationalId.isNotEmpty) {
postParams["P_DEP_NATIONAL_ID"] = pDepNationalId;
}
postParams["MemberType"] = 2;
} else {
postParams["P_DEP_NATIONAL_ID"] = "";
postParams["MemberType"] = 1;
}
_cleanWalaPlusParams(postParams);
debugPrint("redeemWalaPlusOffer calling $url with params: $postParams");
return await ApiClient().postJsonForObject(
(response) {
if (response != null) {
debugPrint("redeemWalaPlusOffer response: $response");
dynamic val =
response['WalaPlus_OfferRedeem'] ??
response['walaPlus_OfferRedeem'] ??
response['WalaPlusOfferRedeem'] ??
response['result'];
if (val is String) {
try {
val = json.decode(val);
} catch (e) {
debugPrint("Error decoding WalaPlus_OfferRedeem: $e");
}
}
if (val is Map<String, dynamic>) {
return val;
} else if (val is Map) {
return Map<String, dynamic>.from(val);
}
}
return null;
},
url,
postParams,
);
}
Future<GenericResponseModel?> addWalaPlusFavorite({
Object? retailerId,
List<String>? retailerIds,
BuildContext? context,
String? mobileNumber,
String? userReference,
}) async {
String url = ApiConsts.walaPlusFavoritesAdd;
String pUserName = _resolveUserName();
String pMobileNumber = _resolveMobileNumber(overrideMobile: mobileNumber);
String pUserReference = _resolveUserReference(
overrideUserReference: userReference,
);
List<String> ids = [];
if (retailerIds != null && retailerIds.isNotEmpty) {
ids.addAll(retailerIds);
} else if (retailerId != null) {
if (retailerId is List) {
ids.addAll(retailerId.map((e) => e.toString()));
} else {
ids.add(retailerId.toString());
}
}
Map<String, dynamic> postParams = _getBaseWalaPlusParams();
postParams["P_USER_NAME"] = pUserName;
postParams["UserName"] = pUserName;
if (pMobileNumber.isNotEmpty) {
postParams["MobileNumber"] = pMobileNumber;
}
if (pUserReference.isNotEmpty) {
postParams["WP_User_Reference"] = pUserReference;
}
postParams["WP_Retailer_Ids"] = ids;
if (context != null) {
postParams["LanguageID"] = AppState().getLanguageID(context);
}
if (ApiConsts.isDependetLogin) {
String pDepNationalId = _resolveDepNationalId();
if (pDepNationalId.isNotEmpty) {
postParams["P_DEP_NATIONAL_ID"] = pDepNationalId;
}
postParams["MemberType"] = 2;
} else {
postParams["MemberType"] = 1;
}
_cleanWalaPlusParams(postParams);
debugPrint("addWalaPlusFavorite calling $url with params: $postParams");
return await ApiClient().postJsonForObject(
(response) {
if (response != null) {
debugPrint("addWalaPlusFavorite response: $response");
return GenericResponseModel.fromJson(response);
}
return null;
},
url,
postParams,
);
}
Future<GenericResponseModel?> removeWalaPlusFavorite({
Object? retailerId,
List<String>? retailerIds,
BuildContext? context,
String? mobileNumber,
String? userReference,
}) async {
String url = ApiConsts.walaPlusFavoritesRemove;
String pUserName = _resolveUserName();
String pMobileNumber = _resolveMobileNumber(overrideMobile: mobileNumber);
String pUserReference = _resolveUserReference(
overrideUserReference: userReference,
);
List<String> ids = [];
if (retailerIds != null && retailerIds.isNotEmpty) {
ids.addAll(retailerIds);
} else if (retailerId != null) {
if (retailerId is List) {
ids.addAll(retailerId.map((e) => e.toString()));
} else {
ids.add(retailerId.toString());
}
}
Map<String, dynamic> postParams = _getBaseWalaPlusParams();
postParams["P_USER_NAME"] = pUserName;
postParams["UserName"] = pUserName;
if (pMobileNumber.isNotEmpty) {
postParams["MobileNumber"] = pMobileNumber;
}
if (pUserReference.isNotEmpty) {
postParams["WP_User_Reference"] = pUserReference;
}
postParams["WP_Retailer_Ids"] = ids;
if (context != null) {
postParams["LanguageID"] = AppState().getLanguageID(context);
}
if (ApiConsts.isDependetLogin) {
String pDepNationalId = _resolveDepNationalId();
if (pDepNationalId.isNotEmpty) {
postParams["P_DEP_NATIONAL_ID"] = pDepNationalId;
}
postParams["MemberType"] = 2;
} else {
postParams["MemberType"] = 1;
}
_cleanWalaPlusParams(postParams);
debugPrint("removeWalaPlusFavorite calling $url with params: $postParams");
return await ApiClient().postJsonForObject(
(response) {
if (response != null) {
debugPrint("removeWalaPlusFavorite response: $response");
return GenericResponseModel.fromJson(response);
}
return null;
},
url,
postParams,
);
}
} }

@ -1,11 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/api/api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/consts.dart';
import 'package:mohem_flutter_app/classes/date_uitl.dart';
import 'package:mohem_flutter_app/models/generic_response_model.dart'; import 'package:mohem_flutter_app/models/generic_response_model.dart';
import 'package:mohem_flutter_app/models/get_approves_list_model.dart'; import 'package:mohem_flutter_app/models/get_approves_list_model.dart';
import 'package:mohem_flutter_app/models/get_contact_dff_structure_list.dart'; import 'package:mohem_flutter_app/models/get_contact_dff_structure_list.dart';
@ -26,29 +24,8 @@ import 'package:mohem_flutter_app/models/profile/submit_address_transaction.dart
import 'package:mohem_flutter_app/models/profile/submit_basic_details_transaction_model.dart'; import 'package:mohem_flutter_app/models/profile/submit_basic_details_transaction_model.dart';
import 'package:mohem_flutter_app/models/profile/submit_contact_transaction_list_model.dart'; import 'package:mohem_flutter_app/models/profile/submit_contact_transaction_list_model.dart';
import 'package:mohem_flutter_app/models/profile/submit_phone_transactions.dart'; import 'package:mohem_flutter_app/models/profile/submit_phone_transactions.dart';
import 'package:mohem_flutter_app/models/profile/patient_info_model.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/models/start_phone_approval_process_model.dart'; import 'package:mohem_flutter_app/models/start_phone_approval_process_model.dart';
// The ERP WCF service's DataContractJsonSerializer rejects any DateTime-typed field unless
// it's wrapped as "/Date(ms)/" - confirmed by its own error on this exact call:
// "DateTime content '6/23/1989 12:00:00 AM' does not start with '/Date(' and end with ')/'
// as required for JSON." GET_EMPLOYEE_CONTACTS hands DATE_OF_BIRTH back as a plain
// "M/d/yyyy h:mm:ss a" string, so it has to be converted before going back out as P_BIRTH_DATE.
String? _toWcfJsonDate(rawDate) {
if (rawDate == null) return null;
String raw = rawDate.toString().trim();
if (raw.isEmpty) return null;
if (raw.contains('/Date(')) return raw;
try {
DateTime parsed = DateUtil.convertSimpleStringDateToDate(raw);
int utcMillis = DateTime.utc(parsed.year, parsed.month, parsed.day).millisecondsSinceEpoch;
return "/Date($utcMillis)/";
} catch (_) {
return null;
}
}
class ProfileApiClient { class ProfileApiClient {
static final ProfileApiClient _instance = ProfileApiClient._internal(); static final ProfileApiClient _instance = ProfileApiClient._internal();
@ -56,140 +33,99 @@ class ProfileApiClient {
factory ProfileApiClient() => _instance; factory ProfileApiClient() => _instance;
// GET_EMP_BUSINESS_CARD_INFO requires the literal TokenID below to validate the call -
// it is not the usual session TokenID, so we don't merge AppState().postParamsJson here.
Future<GenericResponseModel?> getEmployeeBusinessCardInfo() async {
String url = "${ApiConsts.erpRest}GET_EMP_BUSINESS_CARD_INFO";
String deviceToken = AppState().getIsHuawei ? AppState().getHuaweiPushToken : (AppState().getDeviceToken ?? "");
Map<String, dynamic> postParams = {
"VersionID": AppState().postParamsObject?.versionID,
"Channel": AppState().postParamsObject?.channel,
"LanguageID": AppState().postParamsObject?.languageID,
"TokenID": "GET_EMP_BUSINESS_CARD_INFO",
"P_USER_NAME": AppState().getUserName ?? AppState().postParamsObject?.userName ?? AppState().postParamsObject?.pUserName,
"DeviceToken": deviceToken,
"DeviceType": Platform.isAndroid ? "android" : "ios",
};
print(jsonEncode(postParams));
return await ApiClient().postJsonForObject(
(json) {
return GenericResponseModel.fromJson(json);
},
url,
postParams,
);
}
Future<List<GetEmployeeContactsList>> getEmployeeContacts() async { Future<List<GetEmployeeContactsList>> getEmployeeContacts() async {
String url = "${ApiConsts.erpRest}GET_EMPLOYEE_CONTACTS"; String url = "${ApiConsts.erpRest}GET_EMPLOYEE_CONTACTS";
Map<String, dynamic> postParams = {"P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999}; Map<String, dynamic> postParams = {
"P_MENU_TYPE": "E",
"P_SELECTED_RESP_ID": -999,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.getEmployeeContactsList ?? []; return responseData.getEmployeeContactsList ?? [];
}, }, url, postParams);
url,
postParams,
);
} }
Future<List<GetEmployeeBasicDetailsList>> getEmployeeBasicDetails() async { Future<List<GetEmployeeBasicDetailsList>> getEmployeeBasicDetails() async {
String url = "${ApiConsts.erpRest}GET_EMPLOYEE_BASIC_DETAILS"; String url = "${ApiConsts.erpRest}GET_EMPLOYEE_BASIC_DETAILS";
Map<String, dynamic> postParams = {"P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999}; Map<String, dynamic> postParams = {
"P_MENU_TYPE": "E",
"P_SELECTED_RESP_ID": -999,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.getEmployeeBasicDetailsList ?? []; return responseData.getEmployeeBasicDetailsList ?? [];
}, }, url, postParams);
url,
postParams,
);
} }
Future<List<GetEmployeePhonesList>> getEmployeePhones() async { Future<List<GetEmployeePhonesList>> getEmployeePhones() async {
String url = "${ApiConsts.erpRest}GET_EMPLOYEE_PHONES"; String url = "${ApiConsts.erpRest}GET_EMPLOYEE_PHONES";
Map<String, dynamic> postParams = {"P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999}; Map<String, dynamic> postParams = {
"P_MENU_TYPE": "E",
"P_SELECTED_RESP_ID": -999,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.getEmployeePhonesList ?? []; return responseData.getEmployeePhonesList ?? [];
}, }, url, postParams);
url,
postParams,
);
} }
Future<List<GetEmployeeAddressList>> getEmployeeAddress() async { Future<List<GetEmployeeAddressList>> getEmployeeAddress() async {
String url = "${ApiConsts.erpRest}GET_EMPLOYEE_ADDRESS"; String url = "${ApiConsts.erpRest}GET_EMPLOYEE_ADDRESS";
Map<String, dynamic> postParams = {"P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999}; Map<String, dynamic> postParams = {
"P_MENU_TYPE": "E",
"P_SELECTED_RESP_ID": -999,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.getEmployeeAddressList ?? []; return responseData.getEmployeeAddressList ?? [];
}, }, url, postParams);
url,
postParams,
);
} }
Future<List<EmployeeDocumentsList>> getEmployeeDocuments() async { Future<List<EmployeeDocumentsList>> getEmployeeDocuments() async {
String url = "${ApiConsts.erpRest}GET_EMPLOYEE_DOCUMENTS"; String url = "${ApiConsts.erpRest}GET_EMPLOYEE_DOCUMENTS";
Map<String, dynamic> postParams = {"P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999}; Map<String, dynamic> postParams = {
"P_MENU_TYPE": "E",
"P_SELECTED_RESP_ID": -999,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.employeeDocumentsList ?? []; return responseData.employeeDocumentsList ?? [];
}, }, url, postParams);
url,
postParams,
);
} }
Future updateEmpImage(img) async { Future updateEmpImage(img) async {
String url = "${ApiConsts.erpRest}UPDATE_EMPLOYEE_IMAGE"; String url = "${ApiConsts.erpRest}UPDATE_EMPLOYEE_IMAGE";
Map<String, dynamic> postParams = {"P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999, "P_IMAGE": img}; Map<String, dynamic> postParams = {"P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999, "P_IMAGE": img};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
// GenericResponseModel? responseData = GenericResponseModel.fromJson(json); // GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return json['UpdateEmployeeImageList']; return json['UpdateEmployeeImageList'];
}, }, url, postParams);
url,
postParams,
);
} }
Future<GenericResponseModel> getDffStructure(String pFunctionName, String uRL, String requestType) async { Future<GenericResponseModel> getDffStructure(String pFunctionName, String uRL, String requestType) async {
String url = ApiConsts.erpRest + uRL; String url = ApiConsts.erpRest + uRL;
Map<String, dynamic> postParams = {"P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E", "P_REQUEST_TYPE": pFunctionName, "P_FUNCTION_NAME": pFunctionName, "P_COUNTRY_CODE": "SA"}; Map<String, dynamic> postParams = {"P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E", "P_REQUEST_TYPE": pFunctionName, "P_FUNCTION_NAME": pFunctionName, "P_COUNTRY_CODE": "SA"};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData; return responseData;
}, }, url, postParams);
url,
postParams,
);
} }
Future<GenericResponseModel> getColStructure(String pFunctionName, String uRL, String requestType) async { Future<GenericResponseModel> getColStructure(String pFunctionName, String uRL, String requestType) async {
String url = ApiConsts.erpRest + uRL; String url = ApiConsts.erpRest + uRL;
Map<String, dynamic> postParams = {"P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E", "P_FUNCTION_NAME": pFunctionName, "P_REQUEST_TYPE": requestType}; Map<String, dynamic> postParams = {"P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E", "P_FUNCTION_NAME": pFunctionName, "P_REQUEST_TYPE": requestType};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData; return responseData;
}, }, url, postParams);
url,
postParams,
);
} }
Future<ESERVICESVS> getValueSetValues(String pSegmentName, String pDescFlexContextCode, String pDescFlexName, List<Map<String, dynamic>> list) async { Future<ESERVICESVS> getValueSetValues(String pSegmentName, String pDescFlexContextCode, String pDescFlexName, List<Map<String, dynamic>> list) async {
@ -206,38 +142,41 @@ class ProfileApiClient {
"GetValueSetValuesTBL": list, "GetValueSetValuesTBL": list,
}; };
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.getValueSetValuesList!.first; return responseData.getValueSetValuesList!.first;
}, }, url, postParams);
url,
postParams,
);
} }
Future<List<GetPhoneNumberTypesModel>> getPhoneNumberTypes() async { Future<List<GetPhoneNumberTypesModel>> getPhoneNumberTypes() async {
String url = "${ApiConsts.erpRest}GET_OBJECT_VALUES"; String url = "${ApiConsts.erpRest}GET_OBJECT_VALUES";
Map<String, dynamic> postParams = {"P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999, "P_OBJECT_NAME": "PHONE_TYPE", "P_OBJECT_TYPE": "LOOKUP"}; Map<String, dynamic> postParams = {"P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999, "P_OBJECT_NAME": "PHONE_TYPE", "P_OBJECT_TYPE": "LOOKUP"};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.getObjectValuesList ?? []; return responseData.getObjectValuesList ?? [];
}, }, url, postParams);
url,
postParams,
);
} }
Future<SubmitContactTransactionList?> submetConatctTransaction(String pFunctionName, String actionType, int contactRelationId, String date) async { Future<SubmitContactTransactionList?> submetConatctTransaction(
String pFunctionName,
String actionType,
int contactRelationId,
String date,
) async {
String url = "${ApiConsts.erpRest}SUBMIT_CONTACT_TRANSACTION"; String url = "${ApiConsts.erpRest}SUBMIT_CONTACT_TRANSACTION";
Map<String, dynamic> postParams = { Map<String, dynamic> postParams = {
"P_MENU_TYPE": "E", "P_MENU_TYPE": "E",
"P_SELECTED_RESP_ID": -999, "P_SELECTED_RESP_ID": -999,
"P_FUNCTION_NAME": pFunctionName, "P_FUNCTION_NAME": pFunctionName,
"EITTransactionTBL": [ "EITTransactionTBL": [
{"TRANSACTION_NUMBER": 1, "NAME": "DATE_END", "VARCHAR2_VALUE": null, "NUMBER_VALUE": null, "DATE_VALUE": date}, {
"TRANSACTION_NUMBER": 1,
"NAME": "DATE_END",
"VARCHAR2_VALUE": null,
"NUMBER_VALUE": null,
"DATE_VALUE": date,
}
], ],
"P_CONTACT_RELATIONSHIP_ID": contactRelationId, "P_CONTACT_RELATIONSHIP_ID": contactRelationId,
"P_ACTION": actionType, "P_ACTION": actionType,
@ -245,29 +184,28 @@ class ProfileApiClient {
"LegislationCodeStr": "SA", "LegislationCodeStr": "SA",
}; };
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.submitContactTransactionList; return responseData.submitContactTransactionList;
}, }, url, postParams);
url,
postParams,
);
} }
Future<GetApprovesList?> getApproves(String action, String comments, int transactionId, String itemKey) async { Future<GetApprovesList?> getApproves(String action, String comments, int transactionId, String itemKey) async {
String url = "${ApiConsts.erpRest}GET_APPROVERS_LIST"; String url = "${ApiConsts.erpRest}GET_APPROVERS_LIST";
Map<String, dynamic> postParams = {"P_AME_TRANSACTION_TYPE": "SSHRMS", "P_PAGE_LIMIT": 1000, "P_PAGE_NUM": 1, "P_TRANSACTION_ID": transactionId, "P_ACTION_MODE": action, "P_COMMENTS": comments}; Map<String, dynamic> postParams = {
"P_AME_TRANSACTION_TYPE": "SSHRMS",
"P_PAGE_LIMIT": 1000,
"P_PAGE_NUM": 1,
"P_TRANSACTION_ID": transactionId,
"P_ACTION_MODE": action,
"P_COMMENTS": comments,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
// return responseData.getApprovesList; // return responseData.getApprovesList;
return (responseData.getApprovesList?.length ?? 0) > 0 ? responseData.getApprovesList!.first : null; return (responseData.getApprovesList?.length ?? 0) > 0 ? responseData.getApprovesList!.first : null;
}, }, url, postParams);
url,
postParams,
);
} }
Future<SubmitPhonesTransactionList> submitPhoneNumbers(List<GetEmployeePhonesList> empList) async { Future<SubmitPhonesTransactionList> submitPhoneNumbers(List<GetEmployeePhonesList> empList) async {
@ -276,49 +214,50 @@ class ProfileApiClient {
"P_MENU_TYPE": "E", "P_MENU_TYPE": "E",
"P_SELECTED_RESP_ID": -999, "P_SELECTED_RESP_ID": -999,
"P_FUNCTION_NAME": "HR_PERINFO_SS", "P_FUNCTION_NAME": "HR_PERINFO_SS",
"P_MBL_PHONES_TBL": "P_MBL_PHONES_TBL": empList.map((element) {
empList.map((element) {
return element.toJson(); return element.toJson();
}).toList(), }).toList()
}; };
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return SubmitPhonesTransactionList.fromJson(responseData.submitPhonesTransactionList ?? {}); return SubmitPhonesTransactionList.fromJson(responseData.submitPhonesTransactionList ?? {});
}, }, url, postParams);
url,
postParams,
);
} }
Future<StartPhoneApprovalProcess?> startPhoneApprovalProcess(String action, String comments, String itemKey, int transactionId) async { Future<StartPhoneApprovalProcess?> startPhoneApprovalProcess(String action, String comments, String itemKey, int transactionId) async {
String url = "${ApiConsts.erpRest}START_PHONES_APPROVAL_PROCESS"; String url = "${ApiConsts.erpRest}START_PHONES_APPROVAL_PROCESS";
Map<String, dynamic> postParams = {"P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E", "P_ACTION_MODE": action, "P_COMMENTS": comments, "P_ITEM_KEY": itemKey, "P_TRANSACTION_ID": transactionId}; Map<String, dynamic> postParams = {
"P_SELECTED_RESP_ID": -999,
"P_MENU_TYPE": "E",
"P_ACTION_MODE": action,
"P_COMMENTS": comments,
"P_ITEM_KEY": itemKey,
"P_TRANSACTION_ID": transactionId,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.startPhonesApprovalProcessList; return responseData.startPhonesApprovalProcessList;
}, }, url, postParams);
url,
postParams,
);
} }
Future<StartAddressApprovalProcess?> startAddressApprovalProcess(String action, String comments, String itemKey, int transactionId) async { Future<StartAddressApprovalProcess?> startAddressApprovalProcess(String action, String comments, String itemKey, int transactionId) async {
String url = "${ApiConsts.erpRest}START_ADDRESS_APPROVAL_PROCESS"; String url = "${ApiConsts.erpRest}START_ADDRESS_APPROVAL_PROCESS";
Map<String, dynamic> postParams = {"P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E", "P_ACTION_MODE": action, "P_COMMENTS": comments, "P_ITEM_KEY": itemKey, "P_TRANSACTION_ID": transactionId}; Map<String, dynamic> postParams = {
"P_SELECTED_RESP_ID": -999,
"P_MENU_TYPE": "E",
"P_ACTION_MODE": action,
"P_COMMENTS": comments,
"P_ITEM_KEY": itemKey,
"P_TRANSACTION_ID": transactionId,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.startAddressApprovalProcessList; return responseData.startAddressApprovalProcessList;
}, }, url, postParams);
url,
postParams,
);
} }
Future<SubmitBasicDetailsTransactionList> submitBasicDetailsTransaction(String pDescFlexContextCode, String pFunctionName, List<Map<String, dynamic>> list, int correctOrNew) async { Future<SubmitBasicDetailsTransactionList> submitBasicDetailsTransaction(String pDescFlexContextCode, String pFunctionName, List<Map<String, dynamic>> list, int correctOrNew) async {
@ -334,38 +273,31 @@ class ProfileApiClient {
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
postParams["EITTransactionTBL"] = list; postParams["EITTransactionTBL"] = list;
postParams["EITTransactionTBLModel"] = list; postParams["EITTransactionTBLModel"] = list;
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json); GenericResponseModel responseData = GenericResponseModel.fromJson(json);
return responseData.submitBasicDetTransactionList!; //ESERVICESDV.fromJson(responseData.getDefaultValueList!.toJson()); return responseData.submitBasicDetTransactionList!; //ESERVICESDV.fromJson(responseData.getDefaultValueList!.toJson());
}, }, url, postParams);
url,
postParams,
);
} }
Future<StartAddressApprovalProcess?> startBasicDetailsApprovalProcess(String action, String comments, String itemKey, int transactionId) async { Future<StartAddressApprovalProcess?> startBasicDetailsApprovalProcess(String action, String comments, String itemKey, int transactionId) async {
String url = "${ApiConsts.erpRest}START_BASIC_DET_APPR_PROCESS"; String url = "${ApiConsts.erpRest}START_BASIC_DET_APPR_PROCESS";
Map<String, dynamic> postParams = {"P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E", "P_ACTION_MODE": action, "P_COMMENTS": comments, "P_ITEM_KEY": itemKey, "P_TRANSACTION_ID": transactionId}; Map<String, dynamic> postParams = {
"P_SELECTED_RESP_ID": -999,
"P_MENU_TYPE": "E",
"P_ACTION_MODE": action,
"P_COMMENTS": comments,
"P_ITEM_KEY": itemKey,
"P_TRANSACTION_ID": transactionId,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.startAddressApprovalProcessList; return responseData.startAddressApprovalProcessList;
}, }, url, postParams);
url,
postParams,
);
} }
Future<SubmitAddressTransaction> submitAddressTransaction( Future<SubmitAddressTransaction> submitAddressTransaction(
String pDescFlexContextCode, String pDescFlexContextCode, String pFunctionName, List<Map<String, dynamic>> list, int correctOrNew, String countryCode, String effectiveDate) async {
String pFunctionName,
List<Map<String, dynamic>> list,
int correctOrNew,
String countryCode,
String effectiveDate,
) async {
String url = "${ApiConsts.erpRest}SUBMIT_ADDRESS_TRANSACTION"; String url = "${ApiConsts.erpRest}SUBMIT_ADDRESS_TRANSACTION";
Map<String, dynamic> postParams = { Map<String, dynamic> postParams = {
"P_SELECTED_RESP_ID": -999, "P_SELECTED_RESP_ID": -999,
@ -382,28 +314,23 @@ class ProfileApiClient {
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
postParams["EITTransactionTBL"] = list; postParams["EITTransactionTBL"] = list;
// postParams["EITTransactionTBLModel"] = list; // postParams["EITTransactionTBLModel"] = list;
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json); GenericResponseModel responseData = GenericResponseModel.fromJson(json);
return responseData.submitAddressTransactionList!; //ESERVICESDV.fromJson(responseData.getDefaultValueList!.toJson()); return responseData.submitAddressTransactionList!; //ESERVICESDV.fromJson(responseData.getDefaultValueList!.toJson());
}, }, url, postParams);
url,
postParams,
);
} }
Future<List<GetCountriesListModel>> getCountriesList() async { Future<List<GetCountriesListModel>> getCountriesList() async {
String url = "${ApiConsts.erpRest}GET_COUNTRIES"; String url = "${ApiConsts.erpRest}GET_COUNTRIES";
Map<String, dynamic> postParams = {"P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999}; Map<String, dynamic> postParams = {
"P_MENU_TYPE": "E",
"P_SELECTED_RESP_ID": -999,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.getCountriesList ?? []; return responseData.getCountriesList ?? [];
}, }, url, postParams);
url,
postParams,
);
} }
// add & update family member // add & update family member
@ -411,75 +338,73 @@ class ProfileApiClient {
String url = "${ApiConsts.erpRest}GET_BASIC_DET_DFF_STRUCTURE"; String url = "${ApiConsts.erpRest}GET_BASIC_DET_DFF_STRUCTURE";
Map<String, dynamic> postParams = {"P_FUNCTION_NAME": "HR_PERINFO_SS", "P_MENU_TYPE": "E", "P_REQUEST_TYPE": "CONTACT", "P_SELECTED_RESP_ID": -999}; Map<String, dynamic> postParams = {"P_FUNCTION_NAME": "HR_PERINFO_SS", "P_MENU_TYPE": "E", "P_REQUEST_TYPE": "CONTACT", "P_SELECTED_RESP_ID": -999};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.getBasicDetDffStructureList ?? []; return responseData.getBasicDetDffStructureList ?? [];
}, }, url, postParams);
url,
postParams,
);
} }
// add & update family member // add & update family member
Future<List<GetContactColsStructureList>> getContactColsStructureList(String actionType) async { Future<List<GetContactColsStructureList>> getContactColsStructureList(String actionType) async {
String url = "${ApiConsts.erpRest}GET_CONTACT_COLS_STRUCTURE"; String url = "${ApiConsts.erpRest}GET_CONTACT_COLS_STRUCTURE";
Map<String, dynamic> postParams = {"P_ACTION": actionType, "P_FUNCTION_NAME": "HR_PERINFO_SS"}; Map<String, dynamic> postParams = {
"P_ACTION": actionType,
"P_FUNCTION_NAME": "HR_PERINFO_SS",
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.getContactColsStructureList ?? []; return responseData.getContactColsStructureList ?? [];
}, }, url, postParams);
url,
postParams,
);
} }
// update family member // update family member
Future<List<GetContactDetailsList>> getContactDetailsList(int contactRelationId) async { Future<List<GetContactDetailsList>> getContactDetailsList(int contactRelationId) async {
String url = "${ApiConsts.erpRest}GET_CONTACT_DETAILS"; String url = "${ApiConsts.erpRest}GET_CONTACT_DETAILS";
Map<String, dynamic> postParams = {"P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999, "P_CONTACT_RELATIONSHIP_ID": contactRelationId}; Map<String, dynamic> postParams = {
"P_MENU_TYPE": "E",
"P_SELECTED_RESP_ID": -999,
"P_CONTACT_RELATIONSHIP_ID": contactRelationId,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.getContactDetailsList ?? []; return responseData.getContactDetailsList ?? [];
}, }, url, postParams);
url,
postParams,
);
} }
// add & update family member // add & update family member
Future<List<GetContactDffStructureList>> getContactDffStructureList() async { Future<List<GetContactDffStructureList>> getContactDffStructureList() async {
String url = "${ApiConsts.erpRest}GET_CONTACT_DFF_STRUCTURE"; String url = "${ApiConsts.erpRest}GET_CONTACT_DFF_STRUCTURE";
Map<String, dynamic> postParams = {"P_DESC_FLEX_CONTEXT_CODE": "HMG_ADD_CONTACT_DETAILS", "P_FUNCTION_NAME": "HR_PERINFO_SS", "P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999}; Map<String, dynamic> postParams = {
"P_DESC_FLEX_CONTEXT_CODE": "HMG_ADD_CONTACT_DETAILS",
"P_FUNCTION_NAME": "HR_PERINFO_SS",
"P_MENU_TYPE": "E",
"P_SELECTED_RESP_ID": -999,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.getContactDffStructureList ?? []; return responseData.getContactDffStructureList ?? [];
}, }, url, postParams);
url,
postParams,
);
} }
Future<SubmitContactTransactionList?> submitContactTransactionAddAndUpdate(String actionType, relationId, List<Map<String, dynamic>> list) async { Future<SubmitContactTransactionList?> submitContactTransactionAddAndUpdate(String actionType, relationId, List<Map<String, dynamic>> list) async {
String url = "${ApiConsts.erpRest}SUBMIT_CONTACT_TRANSACTION"; String url = "${ApiConsts.erpRest}SUBMIT_CONTACT_TRANSACTION";
Map<String, dynamic> postParams = {"P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999, "P_FUNCTION_NAME": "HR_PERINFO_SS", "P_ACTION": actionType, "P_CONTACT_RELATIONSHIP_ID": relationId}; Map<String, dynamic> postParams = {
"P_MENU_TYPE": "E",
"P_SELECTED_RESP_ID": -999,
"P_FUNCTION_NAME": "HR_PERINFO_SS",
"P_ACTION": actionType,
"P_CONTACT_RELATIONSHIP_ID": relationId,
};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
postParams["EITTransactionTBL"] = list; postParams["EITTransactionTBL"] = list;
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.submitContactTransactionList; return responseData.submitContactTransactionList;
}, }, url, postParams);
url,
postParams,
);
} }
Future<List<GetPerformanceAppraisalList>> getPerformanceAppraisal() async { Future<List<GetPerformanceAppraisalList>> getPerformanceAppraisal() async {
@ -487,91 +412,9 @@ class ProfileApiClient {
Map<String, dynamic> postParams = {}; Map<String, dynamic> postParams = {};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject((json) {
(json) {
GenericResponseModel? responseData = GenericResponseModel.fromJson(json); GenericResponseModel? responseData = GenericResponseModel.fromJson(json);
return responseData.getPerformanceAppraisalList ?? []; return responseData.getPerformanceAppraisalList ?? [];
}, }, url, postParams);
url,
postParams,
);
}
Future<PatientInfoModel?> getPatientInfo({required String nationalId, required String dob, String? logInTokenID}) async {
// NHIC Service GetPatientInfo only works on Production (hmgwebservices.com)
// String url = "https://hmgwebservices.com/Services/NHIC.svc/REST/GetPatientInfo";
String url = "${ApiConsts.erpRest}GetMemberInfo";
String token = logInTokenID ?? AppState().postParamsObject?.logInTokenID ?? "";
Map<String, dynamic> postParams = {
"PatientIdentificationID": int.tryParse(nationalId.trim()) ?? nationalId.trim(),
"PatientOutSA": 0,
"DOB": dob.trim(),
"LogInTokenID": token,
"PatientMobileNumber": AppState().postParamsObject?.mobileNumber, // added this as per e-services changed
"TokenID": AppState().postParamsObject?.tokenID, // added this as per e-services changed
"LanguageID": AppState().postParamsObject?.languageID, // added this as per e-services changed
"Channel": AppState().postParamsObject?.channel,
"VersionID": AppState().postParamsObject?.versionID,
"UserName": AppState().postParamsObject?.userName,
};
print(jsonEncode(postParams));
return await ApiClient().postJsonForObject(
(json) {
return PatientInfoModel.fromJson(json);
},
url,
postParams,
);
}
Future<GenericResponseModel?> processContactMazayaAccess({
required String accessAction,
int? contactRelationshipId,
int? contactPersonId,
int? personId,
// Same shape as the NHIC-style patient-info call: PatientIdentificationID is the
// dependent's national ID (GetEmployeeContactsList.nATIONALIDENTIFIER), PatientOutSA is
// always 0, and the other two are passed straight through from that same contact's
// P_BIRTH_DATE / P_NHIC_VERIFIED_FLAG response fields.
String? patientIdentificationId,
patientBirthDate,
String? nhicVerifiedFlag,
}) async {
String url = "${ApiConsts.erpRest}PROCESS_CONTACT_MAZAYA_ACCESS";
String action = accessAction.trim().toLowerCase() == "revoke" ? "Revoke" : "Grant";
bool isGrant = action == "Grant";
Map<String, dynamic> postParams = {"P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999, "P_ACCESS_ACTION": action};
if (contactRelationshipId != null) {
postParams["P_CONTACT_RELATIONSHIP_ID"] = contactRelationshipId;
}
if (contactPersonId != null) {
postParams["P_CONTACT_PERSON_ID"] = contactPersonId;
}
if (personId != null) {
postParams["P_PERSON_ID"] = personId;
}
postParams["PatientIdentificationID"] = patientIdentificationId?.replaceAll('-', '').trim();
// Only the Grant call carries these - Revoke's payload stays exactly as it was before
// this feature. P_BIRTH_DATE/P_NHIC_VERIFIED_FLAG are always sent (even as null, if the
// source contact from GET_EMPLOYEE_CONTACTS didn't have a value) once we're granting.
if (isGrant) {
postParams["PatientOutSA"] = 0;
postParams["P_BIRTH_DATE"] = _toWcfJsonDate(patientBirthDate);
postParams["P_NHIC_VERIFIED_FLAG"] = nhicVerifiedFlag?.trim();
if (patientIdentificationId != null && patientIdentificationId.trim().isNotEmpty) {
// National ID comes formatted with dashes (e.g. "2-5616-1545-7") - the backend wants
// the plain digit string (e.g. "2561615457").
}
}
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
return GenericResponseModel.fromJson(json);
},
url,
postParams,
);
} }
} }

@ -3,11 +3,9 @@ import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart'; import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart';
import 'package:mohem_flutter_app/models/itg_forms_models/request_detail_model.dart'; import 'package:mohem_flutter_app/models/itg_forms_models/request_detail_model.dart';
import 'package:mohem_flutter_app/models/generic_response_model.dart';
import 'package:mohem_flutter_app/models/member_information_list_model.dart'; import 'package:mohem_flutter_app/models/member_information_list_model.dart';
import 'package:mohem_flutter_app/models/member_login_list_model.dart'; import 'package:mohem_flutter_app/models/member_login_list_model.dart';
import 'package:mohem_flutter_app/models/my_team/get_employee_subordinates_list.dart'; import 'package:mohem_flutter_app/models/my_team/get_employee_subordinates_list.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_city_model.dart';
import 'package:mohem_flutter_app/models/post_params_model.dart'; import 'package:mohem_flutter_app/models/post_params_model.dart';
import 'package:mohem_flutter_app/models/privilege_list_model.dart'; import 'package:mohem_flutter_app/models/privilege_list_model.dart';
import 'package:mohem_flutter_app/models/worklist_response_model.dart'; import 'package:mohem_flutter_app/models/worklist_response_model.dart';
@ -92,74 +90,13 @@ class AppState {
String get getHuaweiPushToken => _huaweiPushToken; String get getHuaweiPushToken => _huaweiPushToken;
final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 10.2, mobileType: Platform.isAndroid ? "android" : "ios"); final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 9.5, mobileType: Platform.isAndroid ? "android" : "ios");
PostParamsModel get postParamsInitConfig {
if (_postParamsInitConfig.pLegislationCode == null) {
if (_postParams?.pLegislationCode != null) {
_postParamsInitConfig.pLegislationCode = _postParams?.pLegislationCode;
} else if (_memberLoginList?.pLEGISLATIONCODE != null) {
_postParamsInitConfig.pLegislationCode = _memberLoginList?.pLEGISLATIONCODE;
}
}
return _postParamsInitConfig;
}
GetWalaplusDepInfoModel? _walaplusDepInfoModel;
GetWalaplusDepInfoModel? get walaplusDepInfoModel => _walaplusDepInfoModel;
set setWalaplusDepInfoModel(GetWalaplusDepInfoModel? model) => _walaplusDepInfoModel = model;
String? _dependentName;
String? get dependentName => _dependentName;
set setDependentName(String? name) => _dependentName = name;
int? _dependentLoginType;
int? get dependentLoginType => _dependentLoginType;
set setDependentLoginType(int? type) => _dependentLoginType = type;
String? _dependentDob;
String? get dependentDob => _dependentDob;
set setDependentDob(String? dob) => _dependentDob = dob;
String? _authenticationTokenID;
String? get authenticationTokenID => _authenticationTokenID;
set setAuthenticationTokenID(String? token) => _authenticationTokenID = token;
bool? _isEmpLastLoginToDevice;
bool? get isEmpLastLoginToDevice => _isEmpLastLoginToDevice;
set setIsEmpLastLoginToDevice(bool? val) => _isEmpLastLoginToDevice = val;
WalaPlusCityModel? _selectedMazayaCity;
WalaPlusCityModel? get selectedMazayaCity => _selectedMazayaCity;
set setSelectedMazayaCity(WalaPlusCityModel? city) => _selectedMazayaCity = city;
void setPostParamsInitConfig() { void setPostParamsInitConfig() {
isAuthenticated = false; isAuthenticated = false;
isLogged = false; isLogged = false;
_memberLoginList = null; _memberLoginList = null;
_memberInformationList = null; _memberInformationList = null;
_walaplusDepInfoModel = null;
_dependentName = null;
_dependentLoginType = null;
_dependentDob = null;
_authenticationTokenID = null;
_isEmpLastLoginToDevice = null;
_selectedMazayaCity = null;
_postParamsInitConfig.pLegislationCode = null;
_postParams = _postParamsInitConfig; _postParams = _postParamsInitConfig;
} }
@ -173,23 +110,9 @@ class AppState {
this._postParams = _postParams; this._postParams = _postParams;
} }
bool isArabic(context) { bool isArabic(context) => EasyLocalization.of(context)?.locale.languageCode == "ar";
try {
if (context != null) {
return EasyLocalization.of(context)?.locale.languageCode == "ar";
}
} catch (_) {}
return false;
}
int getLanguageID(context) { int getLanguageID(context) => EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2;
try {
if (context != null) {
return EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2;
}
} catch (_) {}
return 2;
}
String? _username; String? _username;
@ -206,13 +129,7 @@ class AppState {
MemberLoginListModel? get memberLoginList => _memberLoginList; MemberLoginListModel? get memberLoginList => _memberLoginList;
set setMemberLoginListModel(MemberLoginListModel? _memberLoginList) { set setMemberLoginListModel(MemberLoginListModel? _memberLoginList) => this._memberLoginList = _memberLoginList;
this._memberLoginList = _memberLoginList;
if (_memberLoginList?.pLEGISLATIONCODE != null) {
_postParamsInitConfig.pLegislationCode = _memberLoginList?.pLEGISLATIONCODE?.trim().toUpperCase();
_postParams?.pLegislationCode = _memberLoginList?.pLEGISLATIONCODE?.trim().toUpperCase();
}
}
MemberInformationListModel? _memberInformationList; MemberInformationListModel? _memberInformationList;

@ -1,5 +1,5 @@
import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart'; import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart'; import 'package:geolocator/geolocator.dart';
@ -18,18 +18,20 @@ class AppPermissions {
} }
static void showErrorLocationDialog(bool isPermissionError, BuildContext context) { static void showErrorLocationDialog(bool isPermissionError, BuildContext context) {
ConfirmBottomSheet.show( showDialog(
context, context: context,
message: LocaleKeys.pleaseProvideLocationPermission.tr(), builder: (cxt) => ConfirmDialog(
confirmText: LocaleKeys.openSettings.tr(), message: "Please provide location permission",
showCancelButton: false, onTap: () {
onConfirm: () {
if (isPermissionError) { if (isPermissionError) {
Geolocator.openAppSettings(); Geolocator.openAppSettings();
} else { } else {
Geolocator.openLocationSettings(); Geolocator.openLocationSettings();
} }
Navigator.pop(context);
// createVacationRule(list);
}, },
),
); );
} }

@ -67,5 +67,4 @@ class MyColors {
static const Color lightGreyIconColor = Color(0xff919191); static const Color lightGreyIconColor = Color(0xff919191);
static const Color selectedBorderColor = Color(0xff37A4BE); static const Color selectedBorderColor = Color(0xff37A4BE);
static const Color mazayaRedColor = Color(0xffED1C2B); static const Color mazayaRedColor = Color(0xffED1C2B);
static const Color hintTextColor = Color(0xff9AA4B2);
} }

@ -1,26 +1,17 @@
import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart'; import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart';
class ApiConsts { class ApiConsts {
// static String baseUrl = "http://10.201.214.101:2027"; // Local server // static String baseUrl = "http://10.200.204.11"; // Local server
// static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA server // static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA server
// static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver
// static String baseUrl = "http://10.201.204.101:2024"; // static String baseUrl = "http://10.201.204.101:2024";
// static String baseUrl = "https://webservices.hmg.com"; // PreProd // static String baseUrl = "https://webservices.hmg.com"; // PreProd
// static String baseUrl = "https://hmgwebservices.com"; // Live server
// static String baseUrl = "https://mohemm.hmg.com"; // New Live server
static String ssoBaseUrl = "https://sso.hmg.com"; // New Live server
//
// static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver
// static String baseUrl = "http://10.201.214.101:2027/";
// static String baseUrl = "http://10.20.200.111:1010/";
static String baseUrl = "https://mohemm.hmg.com"; // New Live server static String baseUrl = "https://mohemm.hmg.com"; // New Live server
//static String eLearningBaseUrl = "https://elearning.hmg.com/moodle_dev/api/"; // Elearning server UAT //static String eLearningBaseUrl = "https://elearning.hmg.com/moodle_dev/api/"; // Elearning server UAT
static String eLearningBaseUrl = static String eLearningBaseUrl = "https://elearning.hmg.com/moodle/api/"; // Elearning server live
"https://elearning.hmg.com/moodle/api/"; // Elearning server live
static String enSATLiveURL =
"https://ensat.hmg.com/auth/autologin"; // ENSAT server live
// static String baseUrl = "http://10.20.200.111:1010/"; // static String baseUrl = "http://10.20.200.111:1010/";
// static String baseUrl = "https://webservices.hmg.com"; // PreProd // static String baseUrl = "https://webservices.hmg.com"; // PreProd
@ -30,30 +21,14 @@ class ApiConsts {
static String baseUrlServices = baseUrl + "/Services/"; // server static String baseUrlServices = baseUrl + "/Services/"; // server
// static String baseUrlServices = "https://api.cssynapses.com/tangheem/"; // Live server // static String baseUrlServices = "https://api.cssynapses.com/tangheem/"; // Live server
static String utilitiesRest = baseUrlServices + "Utilities.svc/REST/"; static String utilitiesRest = baseUrlServices + "Utilities.svc/REST/";
static String authenticationRest = static String authenticationRest = baseUrlServices + "Authentication.svc/REST/";
baseUrlServices + "Authentication.svc/REST/";
static String erpRest = baseUrlServices + "ERP.svc/REST/"; static String erpRest = baseUrlServices + "ERP.svc/REST/";
static String swpRest = baseUrlServices + "SWP.svc/REST/"; static String swpRest = baseUrlServices + "SWP.svc/REST/";
static String user = baseUrlServices + "api/User/"; static String user = baseUrlServices + "api/User/";
static String cocRest = baseUrlServices + "COCWS.svc/REST/"; static String cocRest = baseUrlServices + "COCWS.svc/REST/";
// Dependent Login Endpoints static String ssoAuthRedirection = "https://sso-uat.hmg.com/api/auth/connect"; // UAT
static String memberLoginDependent = erpRest + "MemberLogin_Dependent"; // static String ssoAuthRedirection = "https://sso.hmg.com/api/auth/connect"; // Prod
static String mohemmSendOTPDependent = erpRest + "Mohemm_SendOTP_Dependent";
static String mohemmCheckOTPDependent = erpRest + "Mohemm_CheckOTP_Dependent";
// WalaPlus Endpoints
static String walaPlusCategories = erpRest + "WalaPlus_Categories";
static String walaPlusRetailers = erpRest + "WalaPlus_Retailers";
static String walaPlusRetailersByID = erpRest + "WalaPlus_RetailersByID";
static String walaPlusOfferRedeem = erpRest + "WalaPlus_OfferRedeem";
static String walaPlusFavoritesAdd = erpRest + "WalaPlus_FavoritesAdd";
static String walaPlusFavoritesRemove = erpRest + "WalaPlus_FavoritesRemove";
static String walaPlusCities = erpRest + "WalaPlus_Cities";
static String processContactMazayaAccess =
erpRest + "PROCESS_CONTACT_MAZAYA_ACCESS";
static String ssoAuthRedirection = "$ssoBaseUrl/api/auth/connect";
//Chat //Chat
static String chatServerBaseUrl = "https://apiderichat.hmg.com/"; static String chatServerBaseUrl = "https://apiderichat.hmg.com/";
@ -62,24 +37,16 @@ class ApiConsts {
static String chatHubConnectionUrl = chatServerBaseUrl + "ConnectionChatHub"; static String chatHubConnectionUrl = chatServerBaseUrl + "ConnectionChatHub";
//Groups //Groups
static String getGroupByUserId = static String getGroupByUserId = chatServerBaseApiUrl + "group/getgroupsbyuserid/";
chatServerBaseApiUrl + "group/getgroupsbyuserid/"; static String deleteGroup = chatServerBaseApiUrl + "group/updateGroupIsDeleted/";
static String deleteGroup = static String updateGroupAdmin = chatServerBaseApiUrl + "group/updateGroupAdmin/";
chatServerBaseApiUrl + "group/updateGroupIsDeleted/"; static String getGroupChatHistoryAsync = chatServerBaseApiUrl + "GroupChat/GetGroupChatHistoryAsync/";
static String updateGroupAdmin = static String addGroupsAndUsers = chatServerBaseApiUrl + "group/addgroupandusers/";
chatServerBaseApiUrl + "group/updateGroupAdmin/"; static String updateGroupsAndUsers = chatServerBaseApiUrl + "group/updategroupandusers/";
static String getGroupChatHistoryAsync =
chatServerBaseApiUrl + "GroupChat/GetGroupChatHistoryAsync/";
static String addGroupsAndUsers =
chatServerBaseApiUrl + "group/addgroupandusers/";
static String updateGroupsAndUsers =
chatServerBaseApiUrl + "group/updategroupandusers/";
// static String chatSearchMember = chatLoginTokenUrl + "user/"; // static String chatSearchMember = chatLoginTokenUrl + "user/";
static String chatRecentUrl = static String chatRecentUrl = chatServerBaseApiUrl + "UserChatHistory/"; //For a Mem
chatServerBaseApiUrl + "UserChatHistory/"; //For a Mem static String chatSingleUserHistoryUrl = chatServerBaseApiUrl + "UserChatHistory/";
static String chatSingleUserHistoryUrl =
chatServerBaseApiUrl + "UserChatHistory/";
static String chatMediaImageUploadUrl = chatServerBaseApiUrl + "shared/"; static String chatMediaImageUploadUrl = chatServerBaseApiUrl + "shared/";
static String chatFavUser = chatServerBaseApiUrl + "FavUser/"; static String chatFavUser = chatServerBaseApiUrl + "FavUser/";
static String chatUserImages = chatServerBaseUrl + "empservice/api/employee/"; static String chatUserImages = chatServerBaseUrl + "empservice/api/employee/";
@ -91,63 +58,20 @@ class ApiConsts {
static String marathonBaseUrl = marathonBaseUrlLive; static String marathonBaseUrl = marathonBaseUrlLive;
// static String marathonBaseUrl = marathonBaseUrlUAT; // static String marathonBaseUrl = marathonBaseUrlUAT;
static String marathonBaseUrlServices = "https://marathoon.com/service/"; static String marathonBaseUrlServices = "https://marathoon.com/service/";
static String marathonParticipantLoginUrl = static String marathonParticipantLoginUrl = marathonBaseUrl + "auth/participantlogin";
marathonBaseUrl + "auth/participantlogin";
static String marathonProjectGetUrl = marathonBaseUrl + "Project/Project_Get"; static String marathonProjectGetUrl = marathonBaseUrl + "Project/Project_Get";
static String marathonUpcomingUrl = marathonBaseUrl + "marathon/upcoming/"; static String marathonUpcomingUrl = marathonBaseUrl + "marathon/upcoming/";
static String marathonJoinParticipantUrl = static String marathonJoinParticipantUrl = marathonBaseUrl + "participant/join";
marathonBaseUrl + "participant/join";
static String marathonNextQuestionUrl = marathonBaseUrl + "question/next"; static String marathonNextQuestionUrl = marathonBaseUrl + "question/next";
static String marathonSubmitAnswerUrl = marathonBaseUrl + "question/submit"; static String marathonSubmitAnswerUrl = marathonBaseUrl + "question/submit";
static String marathonQualifiersUrl = marathonBaseUrl + "winner/getWinner/"; static String marathonQualifiersUrl = marathonBaseUrl + "winner/getWinner/";
static String marathonSelectedWinner = static String marathonSelectedWinner = marathonBaseUrl + "winner/getSelectedWinner/";
marathonBaseUrl + "winner/getSelectedWinner/"; static String marathonGetMarathonersCount = marathonBaseUrl + "Participant/GetRemainingParticipants";
static String marathonGetMarathonersCount = static String marathonGetTutorial = marathonBaseUrl + "tutorial/GetTutorialNotification";
marathonBaseUrl + "Participant/GetRemainingParticipants";
static String marathonGetTutorial =
marathonBaseUrl + "tutorial/GetTutorialNotification";
//DummyCards for the UI //DummyCards for the UI
static int tabletMinLength = 500; static int tabletMinLength = 500;
// Notification Center GUIDs for OTP
static String notificationCenterAppGUID =
"VDZreDY5TTNmTDVudzc4aDkwU3ZqcW9OcmI0";
static String notificationCenterLoginServiceGUID =
"D8BD5C76-4B50-4122-81D0-C3AA25DB08A2";
static String notificationCenterForgotPasswordServiceGUID =
"15AA0C70-8DA3-430B-8881-EAFBB508F296";
static MemberType selectedMemberType = MemberType.employee;
static bool get ISDependetLogin => selectedMemberType == MemberType.dependent;
static set ISDependetLogin(bool value) {
selectedMemberType = value ? MemberType.dependent : MemberType.employee;
}
static bool get isDependetLogin => ISDependetLogin;
static set isDependetLogin(bool value) => ISDependetLogin = value;
}
enum MemberType {
employee(1),
dependent(2);
final int value;
const MemberType(this.value);
bool get isEmployee => this == MemberType.employee;
bool get isDependent => this == MemberType.dependent;
static MemberType fromValue(int? value) {
if (value == 2) return MemberType.dependent;
return MemberType.employee;
}
} }
class SharedPrefsConsts { class SharedPrefsConsts {
@ -163,3 +87,5 @@ class SharedPrefsConsts {
static String mohemmWifiPassword = "mohemmWifiPassword"; static String mohemmWifiPassword = "mohemmWifiPassword";
static String editItemForSale = "editItemForSale"; static String editItemForSale = "editItemForSale";
} }

@ -22,7 +22,7 @@ class DateUtil {
return DateTime.now(); return DateTime.now();
} }
static DateTime convertStringToDate(String? date) { static DateTime convertStringToDate(String date) {
// /Date(1585774800000+0300)/ // /Date(1585774800000+0300)/
if (date != null) { if (date != null) {
const start = "/Date("; const start = "/Date(";
@ -34,10 +34,9 @@ class DateUtil {
date.substring(startIndex + start.length, endIndex), date.substring(startIndex + start.length, endIndex),
), ),
); );
} else { } else
return DateTime.now(); return DateTime.now();
} }
}
static DateTime convertSimpleStringDateToDate(String date, {bool isITG = false}) { static DateTime convertSimpleStringDateToDate(String date, {bool isITG = false}) {
// print(date.toUpperCase()); // print(date.toUpperCase());
@ -525,47 +524,14 @@ class DateUtil {
// } // }
// return ""; // return "";
// } // }
static String replaceArabicNumber(String? input) { static String replaceArabicNumber(String input) {
if (input == null || input.isEmpty) return input ?? "";
const english = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; const english = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const arabic = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩']; const arabic = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
const persian = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
for (int i = 0; i < english.length; i++) { for (int i = 0; i < english.length; i++) {
input = input!.replaceAll(arabic[i], english[i]).replaceAll(persian[i], english[i]); input = input.replaceAll(arabic[i], english[i]);
}
return input!;
}
static DateTime? parseFlexibleDateTime(String? dateStr) {
if (dateStr == null || dateStr.trim().isEmpty) return null;
String cleanStr = replaceArabicNumber(dateStr.trim()).replaceAll('/"', '').replaceAll('"', '');
if (cleanStr.contains(' 00:00:00')) {
cleanStr = cleanStr.replaceAll(' 00:00:00', '').trim();
}
DateTime? result = DateTime.tryParse(cleanStr);
if (result != null) return result;
if (cleanStr.contains('/')) {
try {
return DateFormat("yyyy/MM/dd", "en_US").parseLoose(cleanStr);
} catch (_) {}
try {
return DateFormat("dd/MM/yyyy", "en_US").parseLoose(cleanStr);
} catch (_) {}
}
if (cleanStr.contains('-')) {
try {
return DateFormat("yyyy-MM-dd", "en_US").parseLoose(cleanStr);
} catch (_) {}
try {
return DateFormat("dd-MM-yyyy", "en_US").parseLoose(cleanStr);
} catch (_) {}
} }
return null; return input;
} }
} }

@ -1,7 +1,6 @@
import 'dart:developer'; import 'dart:developer';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:permission_handler/permission_handler.dart';
class MyInAppBrowser extends InAppBrowser { class MyInAppBrowser extends InAppBrowser {
final Function onExitCallback; final Function onExitCallback;
@ -31,21 +30,4 @@ class MyInAppBrowser extends InAppBrowser {
void onLoadError(Uri? url, int code, String message) { void onLoadError(Uri? url, int code, String message) {
log("Can't load $url.. Error: $message"); log("Can't load $url.. Error: $message");
} }
@override
Future<PermissionResponse?> onPermissionRequest(PermissionRequest permissionRequest) async {
log("onPermissionRequest: ${permissionRequest.resources}");
try {
var status = await Permission.camera.status;
if (!status.isGranted) {
await Permission.camera.request();
}
} catch (e) {
log("Error checking/requesting camera permission: $e");
}
return PermissionResponse(
resources: permissionRequest.resources,
action: PermissionResponseAction.GRANT,
);
}
} }

@ -2,6 +2,7 @@ import 'dart:io';
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
@ -12,6 +13,7 @@ import 'package:mohem_flutter_app/classes/date_uitl.dart';
import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/models/get_notifications_response_model.dart'; import 'package:mohem_flutter_app/models/get_notifications_response_model.dart';
import 'package:mohem_flutter_app/ui/notifications/notification_details_page.dart'; import 'package:mohem_flutter_app/ui/notifications/notification_details_page.dart';
import 'package:permission_handler/permission_handler.dart';
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
@ -27,14 +29,20 @@ class AppNotifications {
String _huaweiToken = ''; String _huaweiToken = '';
late BuildContext context; late BuildContext context;
bool _isInitialized = false;
Future<void> requestPermissions() async { Future<void> requestPermissions() async {
if (Platform.isIOS) { if (Platform.isIOS) {
await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()?.requestPermissions(alert: true, badge: true, sound: true); await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()?.requestPermissions(alert: true, badge: true, sound: true);
} else if (Platform.isAndroid) { } else if (Platform.isAndroid) {
AndroidFlutterLocalNotificationsPlugin? androidImplementation = flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>(); AndroidFlutterLocalNotificationsPlugin? androidImplementation = flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>();
await androidImplementation?.requestNotificationsPermission(); bool? granted = await androidImplementation?.requestNotificationsPermission();
if (granted == false) {
if (kDebugMode) {
print("-------------------- Permission Granted ------------------------");
print(granted);
}
await Permission.notification.request();
}
} }
} }
@ -45,11 +53,13 @@ class AppNotifications {
this.context = context; this.context = context;
if (!_isInitialized) {
_isInitialized = true;
await requestPermissions(); await requestPermissions();
}
AppState().setDeviceToken = firebaseToken; AppState().setDeviceToken = firebaseToken;
await Permission.notification.isDenied.then((bool value) {
if (value) {
Permission.notification.request();
}
});
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage(); RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) _handleMessage(initialMessage); if (initialMessage != null) _handleMessage(initialMessage);
@ -70,7 +80,7 @@ class AppNotifications {
// await hmsApiAvailability.isHMSAvailable().then((value) async { // await hmsApiAvailability.isHMSAvailable().then((value) async {
if (!(await Utils.isGoogleServicesAvailable())) { if (!(await Utils.isGoogleServicesAvailable())) {
huawei_push.Push.enableLogger(); huawei_push.Push.enableLogger();
await huawei_push.Push.setAutoInitEnabled(true); var result = await huawei_push.Push.setAutoInitEnabled(true);
huawei_push.Push.onNotificationOpenedApp.listen((message) { huawei_push.Push.onNotificationOpenedApp.listen((message) {
// newMessage(toFirebaseRemoteMessage(message)); // newMessage(toFirebaseRemoteMessage(message));

@ -6,6 +6,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:google_api_availability/google_api_availability.dart'; import 'package:google_api_availability/google_api_availability.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';
@ -18,13 +19,9 @@ import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart'; import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart';
import 'package:mohem_flutter_app/widgets/loading_dialog.dart'; import 'package:mohem_flutter_app/widgets/loading_dialog.dart';
import 'package:mohem_flutter_app/widgets/toast/app_toast.dart';
import 'package:nfc_manager/nfc_manager.dart'; import 'package:nfc_manager/nfc_manager.dart';
import 'package:nfc_manager/nfc_manager_android.dart'; import 'package:nfc_manager/platform_tags.dart';
import 'package:nfc_manager/nfc_manager_ios.dart';
// import 'package:nfc_manager/platform_tags.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:mohem_flutter_app/ui/profile/mazaya_access_result_screen.dart';
// ignore_for_file: avoid_annotating_with_dynamic // ignore_for_file: avoid_annotating_with_dynamic
@ -33,25 +30,15 @@ class Utils {
static bool get isLoading => _isLoadingVisible; static bool get isLoading => _isLoadingVisible;
// longDuration is accepted but no longer varies the duration - every caller already used the static void showToast(String message, {bool longDuration = true}) {
// default (true), so AppToast.plainSeconds covers both cases with one fixed value. Fluttertoast.showToast(
// isSuccess: true -> soft green/green (same combo as dependent_details_screen.dart's "Active" msg: message,
// badge); false (default) -> soft red/red (same combo as that screen's "Access Off" badge). toastLength: longDuration ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT,
// Callers showing a value straight from an API response should pass isSuccess: response's gravity: ToastGravity.BOTTOM,
// MessageStatus == 1; everywhere else (validation messages, caught errors) just defaults to timeInSecForIosWeb: 1,
// red, and the handful of hardcoded success confirmations pass isSuccess: true explicitly. backgroundColor: Colors.black54,
// onComplete makes this an action-toast (longer actionSeconds duration + icon shown) instead textColor: Colors.white,
// of a plain info toast - this is the single calling point, every caller should go through fontSize: 13.0,
// here instead of calling AppToast.show directly, so the color/duration/icon rules in one place.
static void showToast(String message, {bool longDuration = true, bool isSuccess = false, VoidCallback? onComplete}) {
AppToast.show(
message,
seconds: onComplete != null ? AppToast.actionSeconds : AppToast.plainSeconds,
showIcon: onComplete != null,
showCountdownText: true,
bgColor: isSuccess ? AppToast.successBgColor : AppToast.errorBgColor,
textColor: isSuccess ? AppToast.successTextColor : AppToast.errorTextColor,
onComplete: onComplete,
); );
} }
@ -107,57 +94,9 @@ class Utils {
return await prefs.setString(key, value); return await prefs.setString(key, value);
} }
static bool isMazayaAccessRevoked(dynamic error) {
if (error == null) return false;
String str = "";
if (error is APIException && error.error != null) {
String errCode = error.error?.errorCode?.toString() ?? "";
String errMsg = error.error?.errorMessage?.toString() ?? "";
str = "$errCode $errMsg ${error.message}";
if (errCode.trim().toUpperCase() == "E4") {
return true;
}
} else {
str = error.toString();
}
String lower = str.toLowerCase();
if (lower.contains("no active grant") ||
lower.contains("grant request") ||
lower.contains("grant was found") ||
lower.contains("grant_request") ||
lower.contains("authorized to access") ||
lower.contains("not authorized") ||
lower.contains("e4:") ||
lower.contains("e4 -") ||
lower.contains("e4 :")) {
return true;
}
if (str.contains("طلب منح") ||
str.contains("منح نشط") ||
str.contains("لا يوجد طلب منح") ||
str.contains("تم إلغاء وصول مزايا") ||
str.contains("إلغاء مزايا") ||
str.contains("غير مصرح لك بالوصول") ||
str.contains("غير مصرح")) {
return true;
}
return false;
}
static void handleException(dynamic exception, cxt, Function(String)? onErrorMessage) { static void handleException(dynamic exception, cxt, Function(String)? onErrorMessage) {
if (isMazayaAccessRevoked(exception)) {
String depName = AppState().dependentName ?? "";
if (depName.isEmpty) {
depName = AppState().walaplusDepInfoModel?.pDependentName ?? "";
}
if (cxt != null) {
Navigator.push(cxt, MaterialPageRoute(builder: (context) => MazayaAccessResultScreen(isGranted: false, dependentName: depName)));
return;
}
}
String errorMessage; String errorMessage;
if (exception is APIException && exception.error?.errorType == 4) { if (exception.error.errorType != null && exception.error.errorType == 4) {
Navigator.pushNamedAndRemoveUntil(cxt, AppRoutes.appUpdateScreen, (_) => false, arguments: exception.error?.errorMessage); Navigator.pushNamedAndRemoveUntil(cxt, AppRoutes.appUpdateScreen, (_) => false, arguments: exception.error?.errorMessage);
} else { } else {
if (exception is APIException) { if (exception is APIException) {
@ -173,11 +112,17 @@ class Utils {
onErrorMessage(errorMessage); onErrorMessage(errorMessage);
} else { } else {
if (!AppState().isAuthenticated) { if (!AppState().isAuthenticated) {
showToast( showDialog(
errorMessage, barrierDismissible: false,
onComplete: () { context: cxt,
builder:
(cxt) => ConfirmDialog(
message: errorMessage,
onTap: () {
Navigator.pushNamedAndRemoveUntil(cxt, AppRoutes.login, (Route<dynamic> route) => false); Navigator.pushNamedAndRemoveUntil(cxt, AppRoutes.login, (Route<dynamic> route) => false);
}, },
onCloseTap: () {},
),
); );
} else { } else {
if (cxt != null) { if (cxt != null) {
@ -195,16 +140,8 @@ class Utils {
} }
static void confirmDialog(cxt, String message, {VoidCallback? onTap}) { static void confirmDialog(cxt, String message, {VoidCallback? onTap}) {
// No onTap means there's nothing to confirm - it's a plain notice, so a self-dismissing
// toast replaces the old "OK just closes" dialog instead of requiring a tap. Every current
// caller of this path passes an error/warning message (catch blocks, location/fake-location
// warnings, session errors) - never success - so red is correct here.
if (onTap == null) {
showToast(message);
} else {
showDialog(context: cxt, builder: (BuildContext cxt) => ConfirmDialog(message: message, onTap: onTap)); showDialog(context: cxt, builder: (BuildContext cxt) => ConfirmDialog(message: message, onTap: onTap));
} }
}
static Widget getNoDataWidget(BuildContext context) { static Widget getNoDataWidget(BuildContext context) {
return Column( return Column(
@ -412,34 +349,15 @@ class Utils {
NfcManager.instance NfcManager.instance
.startSession( .startSession(
onDiscovered: (NfcTag tag) async { onDiscovered: (NfcTag tag) async {
// MifareUltralight f; MifareUltralight f;
// if (Platform.isAndroid) {
// print(tag);
// f = MifareUltralight(tag: tag, identifier: tag.data["nfca"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22);
// } else {
// f = MifareUltralight(tag: tag, identifier: tag.data["mifare"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22);
// }
Uint8List? id;
if (Platform.isAndroid) { if (Platform.isAndroid) {
id = NfcTagAndroid.from(tag)?.id; print(tag);
} else if (Platform.isIOS) { f = MifareUltralight(tag: tag, identifier: tag.data["nfca"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22);
id = MiFareIos.from(tag)?.identifier; } else {
} f = MifareUltralight(tag: tag, identifier: tag.data["mifare"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22);
if (id == null) {
await NfcManager.instance.stopSession();
return;
} }
String identifier = f.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
String identifier = NfcManager.instance.stopSession();
id.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
print(identifier);
await NfcManager.instance.stopSession();
// String identifier = f.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
// NfcManager.instance.stopSession();
onRead(identifier); onRead(identifier);
}, },
pollingOptions: {NfcPollingOption.iso14443}, pollingOptions: {NfcPollingOption.iso14443},
@ -475,9 +393,7 @@ class Utils {
AppState().isLogged = false; AppState().isLogged = false;
AppState().setPostParamsInitConfig(); AppState().setPostParamsInitConfig();
if (chatData != null) { if (chatData != null) {
try {
chatData.disposeData(); chatData.disposeData();
} catch (e) {}
} }
// SharedPreferences prefs = await SharedPreferences.getInstance(); // SharedPreferences prefs = await SharedPreferences.getInstance();
// await prefs.clear(); // await prefs.clear();

@ -13,7 +13,6 @@ import 'package:mohem_flutter_app/ui/chat/manage_group.dart';
import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_create_request.dart'; import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_create_request.dart';
import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_ovr_home.dart'; import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_ovr_home.dart';
import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_request_detailed.dart'; import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_request_detailed.dart';
import 'package:mohem_flutter_app/ui/chatbot/chatbot_screen.dart';
import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart';
import 'package:mohem_flutter_app/ui/landing/itg/change_itg_ad_password_screen.dart'; import 'package:mohem_flutter_app/ui/landing/itg/change_itg_ad_password_screen.dart';
import 'package:mohem_flutter_app/ui/landing/itg/its_add_screen_video_image.dart'; import 'package:mohem_flutter_app/ui/landing/itg/its_add_screen_video_image.dart';
@ -32,15 +31,10 @@ import 'package:mohem_flutter_app/ui/marathon/marathon_screen.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_sponsor_video_screen.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_sponsor_video_screen.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_tutorial_viewer_screen.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_tutorial_viewer_screen.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_waiting_screen.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_waiting_screen.dart';
import 'package:mohem_flutter_app/ui/mazaya/retailers_list_screen.dart';
import 'package:mohem_flutter_app/ui/mazaya/mazaya_discover_screen.dart';
import 'package:mohem_flutter_app/ui/mazaya/saved_retailers_screen.dart';
import 'package:mohem_flutter_app/ui/mazaya/retailer_details_screen.dart';
import 'package:mohem_flutter_app/ui/misc/request_submit_screen.dart'; import 'package:mohem_flutter_app/ui/misc/request_submit_screen.dart';
import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart'; import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart';
import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart'; import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart';
import 'package:mohem_flutter_app/ui/my_attendance/services_menu_list_screen.dart'; import 'package:mohem_flutter_app/ui/my_attendance/services_menu_list_screen.dart';
// import 'package:mohem_flutter_app/ui/my_attendance/my_attendance_screen.dart'; // import 'package:mohem_flutter_app/ui/my_attendance/my_attendance_screen.dart';
import 'package:mohem_flutter_app/ui/my_team/create_request.dart'; import 'package:mohem_flutter_app/ui/my_team/create_request.dart';
import 'package:mohem_flutter_app/ui/my_team/employee_details.dart'; import 'package:mohem_flutter_app/ui/my_team/employee_details.dart';
@ -50,12 +44,8 @@ import 'package:mohem_flutter_app/ui/my_team/subordinate_leave.dart';
import 'package:mohem_flutter_app/ui/my_team/team_members.dart'; import 'package:mohem_flutter_app/ui/my_team/team_members.dart';
import 'package:mohem_flutter_app/ui/my_team/view_attendance.dart'; import 'package:mohem_flutter_app/ui/my_team/view_attendance.dart';
import 'package:mohem_flutter_app/ui/parking_qr/parking_qr.dart'; import 'package:mohem_flutter_app/ui/parking_qr/parking_qr.dart';
import 'package:mohem_flutter_app/ui/parking_qr/parking_qr.dart';
import 'package:mohem_flutter_app/ui/payslip/monthly_pay_slip_screen.dart'; import 'package:mohem_flutter_app/ui/payslip/monthly_pay_slip_screen.dart';
import 'package:mohem_flutter_app/ui/performance/performance_appraisal_screen.dart'; import 'package:mohem_flutter_app/ui/performance/performance_appraisal_screen.dart';
import 'package:mohem_flutter_app/ui/profile/add_dependent_verify_screen.dart';
import 'package:mohem_flutter_app/ui/profile/dependent_details_screen.dart';
import 'package:mohem_flutter_app/ui/profile/mazaya_access_result_screen.dart';
import 'package:mohem_flutter_app/ui/profile/add_update_family_member.dart'; import 'package:mohem_flutter_app/ui/profile/add_update_family_member.dart';
import 'package:mohem_flutter_app/ui/profile/basic_details.dart'; import 'package:mohem_flutter_app/ui/profile/basic_details.dart';
import 'package:mohem_flutter_app/ui/profile/contact_details.dart'; import 'package:mohem_flutter_app/ui/profile/contact_details.dart';
@ -69,7 +59,6 @@ import 'package:mohem_flutter_app/ui/screens/announcements/announcement_details.
import 'package:mohem_flutter_app/ui/screens/announcements/announcements.dart'; import 'package:mohem_flutter_app/ui/screens/announcements/announcements.dart';
import 'package:mohem_flutter_app/ui/screens/child_education/child_education_assistance.dart'; import 'package:mohem_flutter_app/ui/screens/child_education/child_education_assistance.dart';
import 'package:mohem_flutter_app/ui/screens/courses/courses_screen.dart'; import 'package:mohem_flutter_app/ui/screens/courses/courses_screen.dart';
// import 'package:mohem_flutter_app/ui/my_attendance/work_from_home_screen.dart'; // import 'package:mohem_flutter_app/ui/my_attendance/work_from_home_screen.dart';
import 'package:mohem_flutter_app/ui/screens/eit/add_eit.dart'; import 'package:mohem_flutter_app/ui/screens/eit/add_eit.dart';
import 'package:mohem_flutter_app/ui/screens/event_activity/event_activity.dart'; import 'package:mohem_flutter_app/ui/screens/event_activity/event_activity.dart';
@ -94,7 +83,6 @@ import 'package:mohem_flutter_app/ui/work_list/itg_detail_screen.dart';
import 'package:mohem_flutter_app/ui/work_list/work_list_screen.dart'; import 'package:mohem_flutter_app/ui/work_list/work_list_screen.dart';
import 'package:mohem_flutter_app/ui/work_list/worklist_detail_screen.dart'; import 'package:mohem_flutter_app/ui/work_list/worklist_detail_screen.dart';
import 'package:mohem_flutter_app/ui/work_list/worklist_settings.dart'; import 'package:mohem_flutter_app/ui/work_list/worklist_settings.dart';
import 'package:mohem_flutter_app/widgets/app_in_app_webview_screen.dart';
class AppRoutes { class AppRoutes {
static GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); static GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
@ -109,10 +97,6 @@ class AppRoutes {
static const String newPassword = "/newPassword"; static const String newPassword = "/newPassword";
static const String loginVerification = "/loginVerification"; static const String loginVerification = "/loginVerification";
static const String dashboard = "/dashboard"; static const String dashboard = "/dashboard";
static const String retailersListScreen = "/dashboardScreenForDependet";
static const String retailerDetailsScreen = "/walaPlusRetailerDetails";
static const String mazayaDiscover = "/mazayaDiscover";
static const String savedReyailersScreen = "/mazayaSaved";
static const String todayAttendance = "/todayAttendance"; static const String todayAttendance = "/todayAttendance";
static const String subMenuScreen = "/submenuScreen"; static const String subMenuScreen = "/submenuScreen";
static const String addEitScreen = "/addeitScreen"; static const String addEitScreen = "/addeitScreen";
@ -158,9 +142,6 @@ class AppRoutes {
static const String familyMembers = "/familyMembers"; static const String familyMembers = "/familyMembers";
static const String deleteFamilyMember = "/deleteFamilyMember"; static const String deleteFamilyMember = "/deleteFamilyMember";
static const String addUpdateFamilyMember = "/addUpdateFamilyMember"; static const String addUpdateFamilyMember = "/addUpdateFamilyMember";
static const String addDependentVerify = "/addDependentVerify";
static const String dependentDetails = "/dependentDetails";
static const String mazayaAccessResult = "/mazayaAccessResult";
// Mowadhafhi // Mowadhafhi
static const String mowadhafhi = "/mowadhafhi"; static const String mowadhafhi = "/mowadhafhi";
@ -218,7 +199,6 @@ class AppRoutes {
static const String chat = "/chat"; static const String chat = "/chat";
static const String chatDetailed = "/chatDetailed"; static const String chatDetailed = "/chatDetailed";
static const String chatFavoriteUsers = "/chatFavoriteUsers"; static const String chatFavoriteUsers = "/chatFavoriteUsers";
static const String chatBotHome = "/chatBotHome";
//Group Chat //Group Chat
static const String manageGroup = "/manageGroup"; static const String manageGroup = "/manageGroup";
@ -245,24 +225,12 @@ class AppRoutes {
//Parking QR //Parking QR
static const String parkingQr = "/parkingQrScreen"; static const String parkingQr = "/parkingQrScreen";
static const String appInAppWebView = "/appInAppWebView";
static final Map<String, WidgetBuilder> routes = { static final Map<String, WidgetBuilder> routes = {
login: (BuildContext context) => LoginScreen(), login: (BuildContext context) => LoginScreen(),
verifyLogin: (BuildContext context) => VerifyLoginScreen(), verifyLogin: (BuildContext context) => VerifyLoginScreen(),
verifyLastLogin: (BuildContext context) => VerifyLastLoginScreen(), verifyLastLogin: (BuildContext context) => VerifyLastLoginScreen(),
dashboard: (BuildContext context) => DashboardScreen(), dashboard: (BuildContext context) => DashboardScreen(),
retailersListScreen: (BuildContext context) => const RetailersListScreen(),
retailerDetailsScreen: (BuildContext context) => const RetailerDetailsScreen(),
mazayaDiscover: (BuildContext context) => const MazayaDiscoverScreen(),
savedReyailersScreen: (BuildContext context) => const SavedRetailersScreen(),
appInAppWebView: (BuildContext context) {
Object? args = ModalRoute.of(context)?.settings.arguments;
if (args is Map<String, dynamic>) {
return AppInAppWebViewScreen(url: args['url'] ?? "", title: args['title']);
}
return const Scaffold();
},
survey: (BuildContext context) => SurveyScreen(), survey: (BuildContext context) => SurveyScreen(),
advertisement: (BuildContext context) => ITGAdsScreen(), advertisement: (BuildContext context) => ITGAdsScreen(),
@ -270,8 +238,8 @@ class AppRoutes {
newPassword: (BuildContext context) => NewPasswordScreen(), newPassword: (BuildContext context) => NewPasswordScreen(),
forgotPassword: (BuildContext context) => ForgotPasswordScreen(), forgotPassword: (BuildContext context) => ForgotPasswordScreen(),
todayAttendance: (BuildContext context) => TodayAttendanceScreen2(), todayAttendance: (BuildContext context) => TodayAttendanceScreen2(),
//eit //eit
addEitScreen: (BuildContext context) => AddEITScreen(), addEitScreen: (BuildContext context) => AddEITScreen(),
//Work List //Work List
@ -282,6 +250,7 @@ class AppRoutes {
worklistSettings: (BuildContext context) => WorklistSettings(), worklistSettings: (BuildContext context) => WorklistSettings(),
// Leave Balance // Leave Balance
leaveBalance: (BuildContext context) => LeaveBalance(), leaveBalance: (BuildContext context) => LeaveBalance(),
addLeaveBalance: (BuildContext context) => AddLeaveBalanceScreen(), addLeaveBalance: (BuildContext context) => AddLeaveBalanceScreen(),
@ -312,9 +281,6 @@ class AppRoutes {
deleteFamilyMember: (BuildContext context) => DeleteFamilyMember(ModalRoute.of(context)!.settings.arguments as int), deleteFamilyMember: (BuildContext context) => DeleteFamilyMember(ModalRoute.of(context)!.settings.arguments as int),
requestSubmitScreen: (BuildContext context) => RequestSubmitScreen(), requestSubmitScreen: (BuildContext context) => RequestSubmitScreen(),
addUpdateFamilyMember: (BuildContext context) => AddUpdateFamilyMember(), addUpdateFamilyMember: (BuildContext context) => AddUpdateFamilyMember(),
addDependentVerify: (BuildContext context) => const AddDependentVerifyScreen(),
dependentDetails: (BuildContext context) => const DependentDetailsScreen(),
mazayaAccessResult: (BuildContext context) => const MazayaAccessResultScreen(),
//mowadhafhi //mowadhafhi
mowadhafhi: (BuildContext context) => MowadhafhiHome(), mowadhafhi: (BuildContext context) => MowadhafhiHome(),
@ -364,7 +330,6 @@ class AppRoutes {
chat: (BuildContext context) => ChatHome(), chat: (BuildContext context) => ChatHome(),
chatDetailed: (BuildContext context) => ChatDetailScreen(), chatDetailed: (BuildContext context) => ChatDetailScreen(),
chatFavoriteUsers: (BuildContext context) => ChatFavoriteUsersScreen(), chatFavoriteUsers: (BuildContext context) => ChatFavoriteUsersScreen(),
chatBotHome: (BuildContext context) => ChatBotScreen(),
//Group Chat //Group Chat
manageGroup: (BuildContext context) => ManageGroupScreen(), manageGroup: (BuildContext context) => ManageGroupScreen(),
@ -381,16 +346,16 @@ class AppRoutes {
unsafeDeviceScreen: (BuildContext context) => const UnsafeDeviceScreen(), unsafeDeviceScreen: (BuildContext context) => const UnsafeDeviceScreen(),
appUpdateScreen: (BuildContext context) => const AppUpdateScreen(), appUpdateScreen: (BuildContext context) => const AppUpdateScreen(),
childEducation: (BuildContext context) => ChildEducationAssistance(), childEducation: (BuildContext context) => ChildEducationAssistance(),
activityScreen: (BuildContext context) => const EventActivityScreen(), activityScreen:(BuildContext context) => const EventActivityScreen(),
//ETQAN OVR //ETQAN OVR
etqanOvr: (BuildContext context) => EtqanOvrHome(), etqanOvr: (BuildContext context) => EtqanOvrHome(),
etqanCreateRequest: (BuildContext context) => EtqanOvrCreateRequest(), etqanCreateRequest: (BuildContext context) => EtqanOvrCreateRequest(),
etqanGetIncidientRequest: (BuildContext context) => EtqanOvrRequestDetailed(), etqanGetIncidientRequest: (BuildContext context) => EtqanOvrRequestDetailed(),
activityScreen:(BuildContext context) => const EventActivityScreen(),
//Parking QR //Parking QR
parkingQr: (BuildContext context) => const ParkingQrScreen(), parkingQr: (BuildContext context) => const ParkingQrScreen(),
activityScreen: (BuildContext context) => const EventActivityScreen(),
}; };
} }

@ -1,17 +1,14 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:easy_localization/src/public_ext.dart'; import 'package:easy_localization/src/public_ext.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:mohem_flutter_app/api/login_api_client.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:mohem_flutter_app/widgets/otp_widget.dart'; import 'package:mohem_flutter_app/widgets/otp_widget.dart';
import 'package:sms_otp_auto_verify/sms_otp_auto_verify.dart';
final ValueNotifier<String> otpFieldClear = ValueNotifier<String>(""); final ValueNotifier<String> otpFieldClear = ValueNotifier<String>("");
@ -27,6 +24,10 @@ class OtpDialog {
Future<Null>? timer; Future<Null>? timer;
static BuildContext? _context;
static bool? _loading;
OtpDialog(this.context, this.type, this.mobileNo, this.onSuccess, this.onFailure, {required this.onResendCode}); OtpDialog(this.context, this.type, this.mobileNo, this.onSuccess, this.onFailure, {required this.onResendCode});
GlobalKey? verifyAccountForm = GlobalKey<FormState>(); GlobalKey? verifyAccountForm = GlobalKey<FormState>();
@ -38,7 +39,12 @@ class OtpDialog {
TextEditingController digit3 = TextEditingController(text: ""); TextEditingController digit3 = TextEditingController(text: "");
TextEditingController digit4 = TextEditingController(text: ""); TextEditingController digit4 = TextEditingController(text: "");
Map verifyAccountFormValue = {'digit1': '', 'digit2': '', 'digit3': '', 'digit4': ''}; Map verifyAccountFormValue = {
'digit1': '',
'digit2': '',
'digit3': '',
'digit4': '',
};
final focusD1 = FocusNode(); final focusD1 = FocusNode();
final focusD2 = FocusNode(); final focusD2 = FocusNode();
final focusD3 = FocusNode(); final focusD3 = FocusNode();
@ -47,16 +53,14 @@ class OtpDialog {
// ProjectViewModel projectProvider; // ProjectViewModel projectProvider;
String displayTime = ''; String displayTime = '';
String? _code;
dynamic setState; dynamic setState;
bool stopTimer = false; bool stopTimer = false;
bool isCodeExpired = false;
// static String signature; // static String signature;
void displayDialog(BuildContext context) async { void displayDialog(BuildContext context) async {
otpFieldClear.value = ""; return showDialog(
_startListeningForSms();
showDialog(
context: context, context: context,
barrierColor: Colors.black.withOpacity(0.63), barrierColor: Colors.black.withOpacity(0.63),
builder: (context) { builder: (context) {
@ -66,8 +70,7 @@ class OtpDialog {
surfaceTintColor: Colors.transparent, surfaceTintColor: Colors.transparent,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
insetPadding: const EdgeInsets.only(left: 21, right: 21), insetPadding: const EdgeInsets.only(left: 21, right: 21),
child: StatefulBuilder( child: StatefulBuilder(builder: (context, setState) {
builder: (context, setState) {
if (displayTime == '') { if (displayTime == '') {
startTimer(setState); startTimer(setState);
} }
@ -93,20 +96,7 @@ class OtpDialog {
LocaleKeys.otp.tr().toText14(), LocaleKeys.otp.tr().toText14(),
LocaleKeys.verification.tr().toText24(isBold: true), LocaleKeys.verification.tr().toText24(isBold: true),
6.height, 6.height,
Builder( (LocaleKeys.pleaseEnterTheVerificationCodeSentTo.tr() + ' xxxxxxxx' + mobileNo.toString().substring(mobileNo.toString().length - 3)).toText16(),
builder: (context) {
String maskedNumber = "";
if (mobileNo != null) {
String numStr = mobileNo.toString();
if (numStr.length >= 3) {
maskedNumber = ' xxxxxxxx' + numStr.substring(numStr.length - 3);
} else {
maskedNumber = ' ' + numStr;
}
}
return (LocaleKeys.pleaseEnterTheVerificationCodeSentTo.tr() + maskedNumber).toText16();
},
),
18.height, 18.height,
ValueListenableBuilder<String>( ValueListenableBuilder<String>(
builder: (BuildContext context, String value, Widget? child) { builder: (BuildContext context, String value, Widget? child) {
@ -138,12 +128,21 @@ class OtpDialog {
), ),
), ),
10.height, 10.height,
isCodeExpired stopTimer
? Row( ? Row(
children: [ children: [
Expanded(child: LocaleKeys.codeExpire.tr().toText16(color: MyColors.redColor)), Expanded(
child: LocaleKeys.codeExpire.tr().toText16(
color: MyColors.redColor,
),
),
12.width, 12.width,
Image.asset("assets/icons/ic_alarm.png", width: 20, height: 20, color: MyColors.redColor), Image.asset(
"assets/icons/ic_alarm.png",
width: 20,
height: 20,
color: MyColors.redColor,
),
], ],
) )
: RichText( : RichText(
@ -151,7 +150,10 @@ class OtpDialog {
text: LocaleKeys.theVerificationCodeWillExpireIn.tr() + '\n', text: LocaleKeys.theVerificationCodeWillExpireIn.tr() + '\n',
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.darkTextColor, letterSpacing: -0.48), style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.darkTextColor, letterSpacing: -0.48),
children: <TextSpan>[ children: <TextSpan>[
TextSpan(text: displayTime, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.textMixColor, letterSpacing: -0.48)), TextSpan(
text: displayTime,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.textMixColor, letterSpacing: -0.48),
),
], ],
), ),
), ),
@ -161,40 +163,57 @@ class OtpDialog {
valueListenable: otpFieldClear, valueListenable: otpFieldClear,
), ),
18.height, 18.height,
DefaultButton(isCodeExpired ? LocaleKeys.resend.tr() : LocaleKeys.cancel.tr(), () async { DefaultButton(
if (isCodeExpired) { stopTimer ? LocaleKeys.resend.tr() : LocaleKeys.cancel.tr(),
() async {
if (stopTimer) {
hideSMSBox(context); hideSMSBox(context);
onResendCode(); onResendCode();
} else { } else {
stopTimer = true; stopTimer = true;
// onFailure();
hideSMSBox(context); hideSMSBox(context);
} }
}, colors: isCodeExpired ? null : [MyColors.pinkDarkColor, MyColors.pinkDarkColor]), },
colors: stopTimer
? null
: [
MyColors.pinkDarkColor,
MyColors.pinkDarkColor,
], ],
), ),
); ],
},
), ),
); );
}, }),
).then((_) { );
stopTimer = true;
_stopListeningForSms();
}); });
} }
InputDecoration buildInputDecoration(BuildContext context) { InputDecoration buildInputDecoration(BuildContext context) {
return InputDecoration( return InputDecoration(
counterText: " ", counterText: " ",
enabledBorder: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(10)), borderSide: BorderSide(color: Colors.black)), enabledBorder: const OutlineInputBorder(
focusedBorder: OutlineInputBorder(borderRadius: const BorderRadius.all(Radius.circular(10.0)), borderSide: BorderSide(color: Theme.of(context).primaryColor)), borderRadius: BorderRadius.all(Radius.circular(10)),
errorBorder: OutlineInputBorder(borderRadius: const BorderRadius.all(Radius.circular(10.0)), borderSide: BorderSide(color: Colors.red)), borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(color: Theme.of(context).primaryColor),
),
errorBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(color: Colors.red),
),
// focusedErrorBorder: OutlineInputBorder( // focusedErrorBorder: OutlineInputBorder(
// borderRadius: const BorderRadius.all(Radius.circular(10.0)), // borderRadius: const BorderRadius.all(Radius.circular(10.0)),
// borderSide: BorderSide(color: Colors.red), // borderSide: BorderSide(color: Colors.red),
// // borderSide: BorderSide(color: Theme.of(context).colorScheme.error), // // borderSide: BorderSide(color: Theme.of(context).colorScheme.error),
// ), // ),
focusedErrorBorder: OutlineInputBorder(borderRadius: const BorderRadius.all(Radius.circular(10.0)), borderSide: BorderSide(color: Theme.of(context).colorScheme.error)), focusedErrorBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(color: Theme.of(context).colorScheme.error),
),
); );
} }
@ -221,120 +240,40 @@ class OtpDialog {
} }
void startTimer(setState) { void startTimer(setState) {
if (stopTimer) return;
remainingTime--; remainingTime--;
if (stopTimer) return; if (stopTimer) return;
try {
setState(() { setState(() {
displayTime = getSecondsAsDigitalClock(remainingTime); displayTime = getSecondsAsDigitalClock(remainingTime);
}); });
} catch (_) {}
timer = Future.delayed(const Duration(seconds: 1), () { timer = Future.delayed(const Duration(seconds: 1), () {
if (stopTimer) return;
if (remainingTime > 0) { if (remainingTime > 0) {
startTimer(setState); startTimer(setState);
} else { } else {
if (stopTimer) return;
try {
setState(() { setState(() {
isCodeExpired = true;
stopTimer = true; stopTimer = true;
}); });
} catch (_) {}
_stopListeningForSms();
} }
}); });
} }
void hideSMSBox(context) { void hideSMSBox(context) {
stopTimer = true;
_stopListeningForSms();
onFailure(); onFailure();
} }
void _onOtpCallBack(String otpCode, bool? isAutofill) { void _onOtpCallBack(String otpCode, bool? isAutofill) {
if (isCodeExpired) return;
if (otpCode.length == 4) { if (otpCode.length == 4) {
_stopListeningForSms(); // stopTimer = true;
otpFieldClear.value = otpCode; otpFieldClear.value = otpCode;
onSuccess(otpCode, _pinPutController); onSuccess(otpCode, _pinPutController);
} }
} }
void _startListeningForSms() { // static getSignature() async {
if (!Platform.isAndroid) return; // // if (Platform.isAndroid) {
try { // // return await SmsRetriever.getAppSignature();
SmsVerification.startListeningSms() // // } else {
.then((String? message) { // // return null;
if (stopTimer || isCodeExpired) return; // // }
if (message != null && message.isNotEmpty) { // }
debugPrint("SMS OTP Auto-Verify received message: $message");
String? otpCode = extractOtpCode(message);
if (otpCode != null && otpCode.length == 4) {
debugPrint("Auto-filling OTP: $otpCode");
_pinPutController.text = otpCode;
_pinPutController.selection = TextSelection.collapsed(offset: otpCode.length);
_onOtpCallBack(otpCode, true);
}
}
})
.catchError((e) {
debugPrint("Error in startListeningSms: $e");
});
} catch (e) {
debugPrint("Error starting SMS listening: $e");
}
}
void _stopListeningForSms() {
if (!Platform.isAndroid) return;
try {
SmsVerification.stopListening();
} catch (e) {
debugPrint("Error stopping SMS listening: $e");
}
}
static String? extractOtpCode(String? message) {
if (message == null || message.trim().isEmpty) return null;
String normalized = _normalizeDigits(message);
// 1. Try matching with OTP context keyword if available
RegExp contextRegex = RegExp(r'(?:code|otp|رمز|كود|تفعيل|تحقق)[^\d]{0,15}(\d{4})\b', caseSensitive: false);
Match? contextMatch = contextRegex.firstMatch(normalized);
if (contextMatch != null && contextMatch.group(1) != null) {
return contextMatch.group(1);
}
// 2. Try exact 4-digit word boundary match
Iterable<Match> matches = RegExp(r'\b\d{4}\b').allMatches(normalized);
if (matches.isNotEmpty) {
return matches.first.group(0);
}
// 3. Fallback
try {
Iterable<Match> allDigits = RegExp(r'\d{4}').allMatches(normalized);
if (allDigits.isNotEmpty) {
return allDigits.first.group(0);
}
} catch (_) {}
return null;
}
static String _normalizeDigits(String input) {
const String arabic = '٠١٢٣٤٥٦٧٨٩';
const String english = '0123456789';
String res = input;
for (int i = 0; i < arabic.length; i++) {
res = res.replaceAll(arabic[i], english[i]);
}
return res;
}
static Future<String?> getSignature() async {
return await LoginApiClient.getSignature();
}
} }

@ -68,11 +68,10 @@ extension EmailValidator on String {
style: TextStyle(fontSize: 10, fontStyle: fontStyle ?? FontStyle.normal, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.4), style: TextStyle(fontSize: 10, fontStyle: fontStyle ?? FontStyle.normal, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.4),
); );
Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0, TextOverflow? overflow}) => Text( Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0}) => Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
maxLines: (maxLine > 0) ? maxLine : null, maxLines: (maxLine > 0) ? maxLine : null,
overflow: overflow,
style: TextStyle( style: TextStyle(
fontSize: 11, fontSize: 11,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600), fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600),
@ -82,34 +81,21 @@ extension EmailValidator on String {
), ),
); );
Widget toText12({ Widget toText12({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int? maxLine, TextOverflow? overflow}) => Text(
Color? color,
FontWeight? weight,
bool isUnderLine = false,
Color? underlineColor,
double underlineGap = 2,
bool isBold = false,
bool isCenter = false,
int? maxLine,
TextOverflow? overflow,
}) {
Widget text = Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
maxLines: maxLine == 0 ? null : maxLine, maxLines: maxLine == 0 ? null : maxLine,
style: TextStyle(fontSize: 12, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600), color: color ?? MyColors.darkTextColor, letterSpacing: -0.72, overflow: TextOverflow.visible), style: TextStyle(
); fontSize: 12,
fontWeight: isBold ? FontWeight.bold : FontWeight.w600,
if (!isUnderLine) return text; color: color ?? MyColors.darkTextColor,
letterSpacing: -0.72,
return Container( decoration: isUnderLine ? TextDecoration.underline : null,
padding: EdgeInsets.only(bottom: underlineGap), overflow: TextOverflow.visible
decoration: BoxDecoration(border: Border(bottom: BorderSide(color: underlineColor ?? color ?? MyColors.darkTextColor, width: 1))), ),
child: text,
); );
}
Widget toText12Auto({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int? maxLine, TextOverflow? overflow}) => AutoSizeText( Widget toText12Auto({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int? maxLine,TextOverflow? overflow}) => AutoSizeText(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
maxLines: maxLine == 0 ? null : maxLine, maxLines: maxLine == 0 ? null : maxLine,
@ -124,7 +110,16 @@ extension EmailValidator on String {
), ),
); );
Widget toTextAuto({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0, double fontSize = 12, double letterSpacing = -0.72, double height = 1}) => Widget toTextAuto({
Color? color,
bool isUnderLine = false,
bool isBold = false,
bool isCenter = false,
int maxLine = 0,
double fontSize = 12,
double letterSpacing = -0.72,
double height = 1,
}) =>
AutoSizeText( AutoSizeText(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
@ -139,34 +134,24 @@ extension EmailValidator on String {
), ),
); );
Widget toText13({Color? color, FontWeight? weight, bool isUnderLine = false, bool isBold = false, int? maxlines, TextOverflow? overflow}) => Text( Widget toText13({Color? color, bool isUnderLine = false}) => Text(
this, this,
maxLines: maxlines, style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.52, decoration: isUnderLine ? TextDecoration.underline : null),
overflow: overflow,
style: TextStyle(
fontSize: 13,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600),
color: color ?? MyColors.darkTextColor,
letterSpacing: -0.52,
decoration: isUnderLine ? TextDecoration.underline : null,
),
); );
Widget toText14({Color? color, bool isUnderLine = false, bool isBold = false, FontWeight? weight, int? maxlines, TextAlign? textAlign, bool isCenter = false, TextOverflow? overflow}) => Text( Widget toText14({Color? color, bool isUnderLine = false, bool isBold = false, FontWeight? weight, int? maxlines, TextAlign? textAlign, bool isCenter = false}) => Text(
this, this,
textAlign: isCenter ? TextAlign.center : textAlign, textAlign: isCenter ? TextAlign.center : (textAlign ?? TextAlign.left),
maxLines: maxlines, maxLines: maxlines,
style: TextStyle( style: TextStyle(
overflow: overflow,
color: color ?? MyColors.darkTextColor, color: color ?? MyColors.darkTextColor,
fontSize: 14, fontSize: 14,
letterSpacing: -0.48, letterSpacing: -0.48,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600), fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600),
decoration: isUnderLine ? TextDecoration.underline : null, decoration: isUnderLine ? TextDecoration.underline : null),
),
); );
Widget toText16({Color? color, FontWeight? weight, bool isUnderLine = false, bool isBold = false, int? maxlines, double? height, bool isCentered = false}) => Text( Widget toText16({Color? color, bool isUnderLine = false, bool isBold = false, int? maxlines, double? height, bool isCentered = false}) => Text(
this, this,
maxLines: maxlines, maxLines: maxlines,
textAlign: isCentered ? TextAlign.center : null, textAlign: isCentered ? TextAlign.center : null,
@ -175,34 +160,39 @@ extension EmailValidator on String {
fontSize: 16, fontSize: 16,
letterSpacing: -0.64, letterSpacing: -0.64,
height: height, height: height,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600), fontWeight: isBold ? FontWeight.bold : FontWeight.w600,
decoration: isUnderLine ? TextDecoration.underline : null, decoration: isUnderLine ? TextDecoration.underline : null,
), ),
); );
Widget toText17({Color? color, FontWeight? weight, bool isBold = false, bool isCentered = false}) => Text( Widget toText17({Color? color, bool isBold = false, bool isCentered = false}) => Text(
this, this,
textAlign: isCentered ? TextAlign.center : null, textAlign: isCentered ? TextAlign.center : null,
style: TextStyle(color: color ?? MyColors.darkTextColor, fontSize: 17, letterSpacing: -0.68, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600)), style: TextStyle(color: color ?? MyColors.darkTextColor, fontSize: 17, letterSpacing: -0.68, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
); );
Widget toText18({Color? color, FontWeight? weight, bool isBold = false, bool isCentered = false}) => Text( Widget toText18({Color? color, bool isBold = false, bool isCentered = false}) => Text(
this, this,
textAlign: isCentered ? TextAlign.center : null, textAlign: isCentered ? TextAlign.center : null,
style: TextStyle(fontSize: 18, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600), color: color ?? MyColors.darkTextColor, letterSpacing: -1.08), style: TextStyle(fontSize: 18, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -1.08),
); );
Widget toText19({Color? color, bool isBold = false}) => Widget toText19({Color? color, bool isBold = false}) => Text(
Text(this, style: TextStyle(fontSize: 19, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -1.14)); this,
style: TextStyle(fontSize: 19, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -1.14),
);
Widget toText20({Color? color, FontWeight? weight, bool isBold = false, bool isCentered = false}) => Text( Widget toText20({Color? color, bool isBold = false, bool isCentered = false}) => Text(
this, this,
textAlign: isCentered ? TextAlign.center : null, textAlign: isCentered ? TextAlign.center : null,
style: TextStyle(fontSize: 20, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600), color: color ?? MyColors.darkTextColor, letterSpacing: -0.4), style: TextStyle(fontSize: 20, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.4),
); );
Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Text(
Text(this, maxLines: maxlines, style: TextStyle(color: color ?? MyColors.grey3AColor, fontSize: 21, letterSpacing: -0.84, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600))); this,
maxLines: maxlines,
style: TextStyle(color: color ?? MyColors.grey3AColor, fontSize: 21, letterSpacing: -0.84, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600)),
);
Widget toText22({Color? color, bool isBold = false, bool isCentered = false}) => Text( Widget toText22({Color? color, bool isBold = false, bool isCentered = false}) => Text(
this, this,
@ -234,8 +224,10 @@ extension EmailValidator on String {
style: TextStyle(height: 32 / 32, color: color ?? MyColors.darkTextColor, fontSize: 32, letterSpacing: -1.92, fontWeight: isBold ? FontWeight.bold : FontWeight.w600), style: TextStyle(height: 32 / 32, color: color ?? MyColors.darkTextColor, fontSize: 32, letterSpacing: -1.92, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
); );
Widget toText44({Color? color, bool isBold = false}) => Widget toText44({Color? color, bool isBold = false}) => Text(
Text(this, style: TextStyle(height: 32 / 32, color: color ?? MyColors.darkTextColor, fontSize: 44, letterSpacing: -2.64, fontWeight: isBold ? FontWeight.bold : FontWeight.w600)); this,
style: TextStyle(height: 32 / 32, color: color ?? MyColors.darkTextColor, fontSize: 44, letterSpacing: -2.64, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
);
Widget toSectionHeading({String upperHeading = "", String lowerHeading = ""}) { Widget toSectionHeading({String upperHeading = "", String lowerHeading = ""}) {
String upper = ""; String upper = "";
@ -259,7 +251,14 @@ extension EmailValidator on String {
lower = lowerHeading; lower = lowerHeading;
} }
return Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [if (upper.isNotEmpty) upper.toText12(), lower.toText24(isBold: true)]); return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (upper.isNotEmpty) upper.toText12(),
lower.toText24(isBold: true),
],
);
} }
bool isValidEmail() { bool isValidEmail() {

File diff suppressed because it is too large Load Diff

@ -323,8 +323,6 @@ abstract class LocaleKeys {
static const createdFor = 'createdFor'; static const createdFor = 'createdFor';
static const requestCreatedSuccessfully = 'requestCreatedSuccessfully'; static const requestCreatedSuccessfully = 'requestCreatedSuccessfully';
static const search = 'search'; static const search = 'search';
static const filterEnterPrDetailsHere = 'filterEnterPrDetailsHere';
static const filterEnterPoDetailsHere = 'filterEnterPoDetailsHere';
static const wantToReject = 'wantToReject'; static const wantToReject = 'wantToReject';
static const requestType = 'requestType'; static const requestType = 'requestType';
static const employeeDigitalID = 'employeeDigitalID'; static const employeeDigitalID = 'employeeDigitalID';
@ -590,15 +588,6 @@ abstract class LocaleKeys {
static const addUsers = 'addUsers'; static const addUsers = 'addUsers';
static const admin = 'admin'; static const admin = 'admin';
static const areYouSureWantTodelete = 'areYouSureWantTodelete'; static const areYouSureWantTodelete = 'areYouSureWantTodelete';
static const confirmGrantMazayaAccess = 'confirmGrantMazayaAccess';
static const confirmRevokeMazayaAccess = 'confirmRevokeMazayaAccess';
static const yes = 'yes';
static const no = 'no';
static const mazayaInviteInstructions = 'mazayaInviteInstructions';
static const cameraPermissionRequiredToScan = 'cameraPermissionRequiredToScan';
static const cameraPermissionPermanentlyDenied = 'cameraPermissionPermanentlyDenied';
static const openSettings = 'openSettings';
static const nationalIdOrIqamaFallback = 'nationalIdOrIqamaFallback';
static const Attendance = 'Attendance'; static const Attendance = 'Attendance';
static const audioCall = 'audioCall'; static const audioCall = 'audioCall';
static const editGroups = 'editGroups'; static const editGroups = 'editGroups';
@ -667,209 +656,5 @@ abstract class LocaleKeys {
static const viewAttachment = 'viewAttachment'; static const viewAttachment = 'viewAttachment';
static const parkingQr = 'parkingQr'; static const parkingQr = 'parkingQr';
static const startNow = 'startNow'; static const startNow = 'startNow';
static const destination_type = 'destination_type';
static const source_organization_name = 'source_organization_name';
static const destination_organization_name = 'destination_organization_name';
static const costCenter = 'costCenter';
static const priceSAR = 'priceSAR';
static const amountSAR = 'amountSAR';
static const amuLastThreeMonths = 'amuLastThreeMonths';
static const mazayaTitle = 'mazayaTitle';
static const mazayaAccessGranted = 'mazayaAccessGranted';
static const mazayaAccessGrantedDesc = 'mazayaAccessGrantedDesc';
static const accessIsNowActive = 'accessIsNowActive';
static const accessIsNowActiveDesc = 'accessIsNowActiveDesc';
static const dependentAccessActive = 'dependentAccessActive';
static const shareableAppDownloadLink = 'shareableAppDownloadLink';
static const shareableAppDownloadLinkDesc = 'shareableAppDownloadLinkDesc';
static const downloadLink = 'downloadLink';
static const shareDownloadLink = 'shareDownloadLink';
static const copyLink = 'copyLink';
static const done = 'done';
static const linkCopied = 'linkCopied';
static const mazayaAccessRevoked = 'mazayaAccessRevoked';
static const mazayaAccessRevokedDesc = 'mazayaAccessRevokedDesc';
static const contactYourSponsor = 'contactYourSponsor';
static const contactYourSponsorDesc = 'contactYourSponsorDesc';
static const dependentAccessBlocked = 'dependentAccessBlocked';
static const returnToSignIn = 'returnToSignIn';
static const allowMazayaAccess = 'allowMazayaAccess';
static const revokeMazayaAccess = 'revokeMazayaAccess';
static const active = 'active';
static const noAccess = 'noAccess';
static const offerDetails = 'offerDetails';
static const offerIdNotAvailable = 'offerIdNotAvailable';
static const unableToRedeemOffer = 'unableToRedeemOffer';
static const errorRedeemingOffer = 'errorRedeemingOffer';
static const useOffer = 'useOffer';
static const online = 'online';
static const inStore = 'inStore';
static const ksa = 'ksa';
static const riyadh = 'riyadh';
static const noInstructionsAvailable = 'noInstructionsAvailable';
static const contactInfo = 'contactInfo';
static const allCategories = 'allCategories';
static const noOffersAvailable = 'noOffersAvailable';
static const addDependents = 'addDependents';
static const myDependents = 'myDependents';
static const offDiscount = 'offDiscount';
static const usageLabel = 'usageLabel';
static const areYouSureWantToLogOut = 'areYouSureWantToLogOut';
static const searchDependents = 'searchDependents';
static const searchFamilyMembers = 'searchFamilyMembers';
static const addAnotherUser = 'addAnotherUser';
static const joinMazaya = 'joinMazaya';
static const joinMazayaSubtitle = 'joinMazayaSubtitle';
static const continueAsEmployee = 'continueAsEmployee';
static const continueAsEmployeeSubtitle = 'continueAsEmployeeSubtitle';
static const pleaseEnterNationalId = 'pleaseEnterNationalId';
static const pleaseSelectDob = 'pleaseSelectDob';
static const unableToRetrieveDependentInfo = 'unableToRetrieveDependentInfo';
static const failedToSendOtp = 'failedToSendOtp';
static const verificationCodeResent = 'verificationCodeResent';
static const nationalId = 'nationalId';
static const addDependent = 'addDependent';
static const stepVerify = 'stepVerify';
static const stepBasic = 'stepBasic';
static const stepAdditional = 'stepAdditional';
static const findYourDependent = 'findYourDependent';
static const findYourDependentDesc = 'findYourDependentDesc';
static const enter10DigitNationalId = 'enter10DigitNationalId';
static const nationalIdMustBe10Digits = 'nationalIdMustBe10Digits';
static const dateFormatPlaceholder = 'dateFormatPlaceholder';
static const searchUser = 'searchUser';
static const dependentNotFoundNhic = 'dependentNotFoundNhic';
static const basicInfo = 'basicInfo';
static const basicInfoDesc = 'basicInfoDesc';
static const additionalInfo = 'additionalInfo';
static const additionalInfoDesc = 'additionalInfoDesc';
static const select = 'select';
static const enterFieldPlaceholder = 'enterFieldPlaceholder';
static const requiredField = 'requiredField';
static const pleaseFillField = 'pleaseFillField';
static const reviewAndSubmit = 'reviewAndSubmit';
static const reviewAndSubmitDesc = 'reviewAndSubmitDesc';
static const arabicName = 'arabicName';
static const relationship = 'relationship';
static const sponsor = 'sponsor';
static const dependentSponsorDetail = 'dependentSponsorDetail';
static const insuranceEligibility = 'insuranceEligibility';
static const relationshipStartDate = 'relationshipStartDate';
static const dependentAddedSuccessfully = 'dependentAddedSuccessfully';
static const genderMale = 'genderMale';
static const genderFemale = 'genderFemale';
static const religionIslam = 'religionIslam';
static const back = 'back';
static const submitRequest = 'submitRequest';
static const enterDependentDetailsToContinue = 'enterDependentDetailsToContinue';
static const enterBasicDetailsDesc = 'enterBasicDetailsDesc';
static const locations = 'locations';
static const availableOffers = 'availableOffers';
static const selectOfferToRedeem = 'selectOfferToRedeem';
static const viewOnMap = 'viewOnMap';
static const showAllLocations = 'showAllLocations';
static const searchBranches = 'searchBranches';
static const allCities = 'allCities';
static const allCitiesOption = 'allCitiesOption';
static const noBranchesFound = 'noBranchesFound';
static const browseAllBranches = 'browseAllBranches';
static const branchesCount = 'branchesCount';
static const kmAway = 'kmAway';
static const dependentDetails = 'dependentDetails';
static const keyDetails = 'keyDetails';
static const verified = 'verified';
static const idEnding = 'idEnding';
static const servicesAndActions = 'servicesAndActions';
static const startDependentFlowsDesc = 'startDependentFlowsDesc';
static const mazayaAccess = 'mazayaAccess';
static const accessOff = 'accessOff';
static const allowDependentMazayaOffersDesc = 'allowDependentMazayaOffersDesc';
static const loginMethod = 'loginMethod';
static const nationalIdAndDob = 'nationalIdAndDob';
static const sponsorOtp = 'sponsorOtp';
static const revokingPreventsAccessDesc = 'revokingPreventsAccessDesc';
static const locationPermissionRequiredForAttendance = 'locationPermissionRequiredForAttendance';
static const locationServicesRequiredForAttendance = 'locationServicesRequiredForAttendance';
static const pleaseProvideLocationPermission = 'pleaseProvideLocationPermission';
static const microphoneAccessRequired = 'microphoneAccessRequired';
static const shareAccessGuide = 'shareAccessGuide';
static const nationalIdOrIqamaNumber = 'nationalIdOrIqamaNumber';
static const multipleCities = 'multipleCities';
static const filters = 'filters';
static const availability = 'availability';
static const chooseOneOrBoth = 'chooseOneOrBoth';
static const sortBy = 'sortBy';
static const recommended = 'recommended';
static const nearest = 'nearest';
static const highestDiscount = 'highestDiscount';
static const newest = 'newest';
static const distance = 'distance';
static const fromYourCurrentLocation = 'fromYourCurrentLocation';
static const any = 'any';
static const minimumDiscount = 'minimumDiscount';
static const reset = 'reset';
static const applyFilters = 'applyFilters';
static const distanceKm = 'distanceKm';
static const discountPercentPlus = 'discountPercentPlus';
static const visitMerchantWebsite = 'visitMerchantWebsite';
static const favoriteAddedSuccess = 'favoriteAddedSuccess';
static const favoriteRemovedSuccess = 'favoriteRemovedSuccess';
static const favoriteError = 'favoriteError';
static const merchantWebsiteNotAvailable = 'merchantWebsiteNotAvailable';
static const locationServicesRequiredForNearestOffers = 'locationServicesRequiredForNearestOffers';
static const locationPermissionRequiredForNearestOffers = 'locationPermissionRequiredForNearestOffers';
static const mazayaDeals = 'mazayaDeals';
static const helloName = 'helloName';
static const findABrandOrSomething = 'findABrandOrSomething';
static const exploreYourBenefits = 'exploreYourBenefits';
static const theMazayaEdit = 'theMazayaEdit';
static const exclusivelyYours = 'exclusivelyYours';
static const aLittleExtraJustForMazayaMembers = 'aLittleExtraJustForMazayaMembers';
static const seeAll = 'seeAll';
static const latestOnMazaya = 'latestOnMazaya';
static const goodFoodGreatCompanyEvenBetterPerks = 'goodFoodGreatCompanyEvenBetterPerks';
static const travelEssentials = 'travelEssentials';
static const fromEverydayCareToYourNextAdventure = 'fromEverydayCareToYourNextAdventure';
static const homeAndKitchen = 'homeAndKitchen';
static const curatedPicksForYourHomeAndKitchen = 'curatedPicksForYourHomeAndKitchen';
static const discover = 'discover';
static const saved = 'saved';
static const myFamily = 'myFamily';
static const educationAndTraining = 'educationAndTraining';
static const foodsAndDrinks = 'foodsAndDrinks';
static const travelAndEntertainment = 'travelAndEntertainment';
static const beautyAndFitness = 'beautyAndFitness';
static const healthcare = 'healthcare';
static const favoriteMerchant = 'favoriteMerchant';
static const favoriteOffers = 'favoriteOffers';
static const goodToKnow = 'goodToKnow';
static const goodToKnowDesc = 'goodToKnowDesc';
static const termsAndConditions = 'termsAndConditions';
static const haveAQuestionAboutOffer = 'haveAQuestionAboutOffer';
static const getHelp = 'getHelp';
static const redeemOffer = 'redeemOffer';
static const merchantContact = 'merchantContact';
static const website = 'website';
static const phone = 'phone';
static const viewAll = 'viewAll';
static const moreCitiesCount = 'moreCitiesCount';
static const findAnEscapeOrAnExperience = 'findAnEscapeOrAnExperience';
static const allOffers = 'allOffers';
static const sampleAvailability = 'sampleAvailability';
static const offersCount = 'offersCount';
static const mazayaExclusive = 'mazayaExclusive';
static const youAreAllCaughtUp = 'youAreAllCaughtUp';
static const selectCity = 'selectCity';
static const searchCities = 'searchCities';
static const chatBot = 'chatBot';
static const helloAskMe = 'helloAskMe';
static const anything = 'anything';
static const thinking = 'thinking';
static const waitingForResponse = 'waitingForResponse';
static const typeAMessage = 'typeAMessage';
static const initializingChatPleaseWait = 'initializingChatPleaseWait';
static const am = 'am';
static const pm = 'pm';
static const carServices = 'carServices';
} }

@ -8,6 +8,7 @@
import 'package:audio_session/audio_session_web.dart'; import 'package:audio_session/audio_session_web.dart';
import 'package:camera_web/camera_web.dart'; import 'package:camera_web/camera_web.dart';
import 'package:file_picker/_internal/file_picker_web.dart';
import 'package:firebase_core_web/firebase_core_web.dart'; import 'package:firebase_core_web/firebase_core_web.dart';
import 'package:firebase_messaging_web/firebase_messaging_web.dart'; import 'package:firebase_messaging_web/firebase_messaging_web.dart';
import 'package:fluttertoast/fluttertoast_web.dart'; import 'package:fluttertoast/fluttertoast_web.dart';
@ -26,6 +27,7 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart';
void registerPlugins(Registrar registrar) { void registerPlugins(Registrar registrar) {
AudioSessionWeb.registerWith(registrar); AudioSessionWeb.registerWith(registrar);
CameraPlugin.registerWith(registrar); CameraPlugin.registerWith(registrar);
FilePickerWeb.registerWith(registrar);
FirebaseCoreWeb.registerWith(registrar); FirebaseCoreWeb.registerWith(registrar);
FirebaseMessagingWeb.registerWith(registrar); FirebaseMessagingWeb.registerWith(registrar);
FluttertoastWebPlugin.registerWith(registrar); FluttertoastWebPlugin.registerWith(registrar);

@ -19,8 +19,6 @@ import 'package:month_year_picker/month_year_picker.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart'; import 'package:provider/single_child_widget.dart';
import 'package:sizer/sizer.dart'; import 'package:sizer/sizer.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb;
// test uat account // test uat account
// username 199067 // username 199067
@ -79,7 +77,7 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Sizer( return Sizer(
builder: (BuildContext context, Orientation orientation, ScreenType deviceType){ builder: (BuildContext context, Orientation orientation, DeviceType deviceType) {
PostParamsModel? obj = AppState().postParamsObject; PostParamsModel? obj = AppState().postParamsObject;
obj?.languageID = EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2; obj?.languageID = EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2;
AppState().setPostParamsModel(obj!); AppState().setPostParamsModel(obj!);
@ -103,9 +101,8 @@ class MyApp extends StatelessWidget {
routes: AppRoutes.routes, routes: AppRoutes.routes,
), ),
); );
} },
); );
} }
} }

@ -1,7 +1,7 @@
import 'dart:convert'; import 'dart:convert';
class GetTicketAccuralBalanceModel { class GetTicketAccuralBalanceModel {
num? accrualNetEntitlement; int? accrualNetEntitlement;
String? accuralPlanCode; String? accuralPlanCode;
GetTicketAccuralBalanceModel({ GetTicketAccuralBalanceModel({

@ -1,92 +0,0 @@
class GetMobileLoginInfoDependentModel {
int? iD;
String? dependentID;
String? depedentName;
String? dob;
int? channelID;
int? companyID;
String? deviceType;
String? deviceToken;
int? language;
int? gender;
int? loginType;
String? createdOn;
String? editedOn;
bool? businessCardPrivilege;
GetMobileLoginInfoDependentModel({
this.iD,
this.dependentID,
this.depedentName,
this.dob,
this.channelID,
this.companyID,
this.deviceType,
this.deviceToken,
this.language,
this.gender,
this.loginType,
this.createdOn,
this.editedOn,
this.businessCardPrivilege,
});
// Convenience aliases for existing logic
String? get nationalId => dependentID;
String? get birthDate => dob;
String? get employeeName => depedentName;
String? get dependentName => depedentName;
GetMobileLoginInfoDependentModel.fromJson(Map<String, dynamic> json) {
iD = json['ID'];
dependentID = (json['DependentID'] ??
json['DEPENDENT_ID'] ??
json['P_DEP_NATIONAL_ID'] ??
json['NationalID'] ??
json['NationalId'] ??
json['EmployeeID'] ??
json['UserName'])
?.toString();
depedentName = (json['DepedentName'] ??
json['DependentName'] ??
json['P_DEPENDENT_NAME'] ??
json['EmployeeName'])
?.toString();
dob = (json['DOB'] ??
json['BirthDate'] ??
json['DateOfBirth'] ??
json['P_DEP_BIRTH_DATE'] ??
json['P_BIRTH_DATE'] ??
json['Birth_Date'])
?.toString();
channelID = json['ChannelID'] != null ? int.tryParse(json['ChannelID'].toString()) : null;
companyID = json['CompanyID'] != null ? int.tryParse(json['CompanyID'].toString()) : null;
deviceType = json['DeviceType']?.toString();
deviceToken = json['DeviceToken']?.toString();
language = json['Language'] != null ? int.tryParse(json['Language'].toString()) : null;
gender = json['Gender'] != null ? int.tryParse(json['Gender'].toString()) : null;
loginType = json['LoginType'] != null ? int.tryParse(json['LoginType'].toString()) : null;
createdOn = json['CreatedOn']?.toString();
editedOn = json['EditedOn']?.toString();
businessCardPrivilege = json['BusinessCardPrivilege'];
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{};
data['ID'] = iD;
data['DependentID'] = dependentID;
data['DepedentName'] = depedentName;
data['DOB'] = dob;
data['ChannelID'] = channelID;
data['CompanyID'] = companyID;
data['DeviceType'] = deviceType;
data['DeviceToken'] = deviceToken;
data['Language'] = language;
data['Gender'] = gender;
data['LoginType'] = loginType;
data['CreatedOn'] = createdOn;
data['EditedOn'] = editedOn;
data['BusinessCardPrivilege'] = businessCardPrivilege;
return data;
}
}

@ -30,7 +30,7 @@ class CollectionTransaction {
String? dISPLAYFLAG; String? dISPLAYFLAG;
int? fROMROWNUM; int? fROMROWNUM;
int? nOOFROWS; int? nOOFROWS;
dynamic nUMBERVALUE; dynamic? nUMBERVALUE;
int? rOWNUM; int? rOWNUM;
String? sEGMENTNAME; String? sEGMENTNAME;
String? sEGMENTPROMPT; String? sEGMENTPROMPT;
@ -80,7 +80,7 @@ class CollectionTransaction {
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
Map<String, dynamic> data = Map<String, dynamic>(); Map<String, dynamic> data = new Map<String, dynamic>();
data['APPLICATION_COLUMN_NAME'] = this.aPPLICATIONCOLUMNNAME; data['APPLICATION_COLUMN_NAME'] = this.aPPLICATIONCOLUMNNAME;
data['DATATYPE'] = this.dATATYPE; data['DATATYPE'] = this.dATATYPE;
data['DATE_VALUE'] = this.dATEVALUE; data['DATE_VALUE'] = this.dATEVALUE;

@ -1,97 +0,0 @@
import 'dart:convert';
class GetEitAttachmentTicket {
List<GetEitAttachmentList>? getEitAttachmentList;
GetEitAttachmentTicket({
this.getEitAttachmentList,
});
factory GetEitAttachmentTicket.fromRawJson(String str) => GetEitAttachmentTicket.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory GetEitAttachmentTicket.fromJson(Map<String, dynamic> json) => GetEitAttachmentTicket(
getEitAttachmentList: json["GetEITAttachmentList"] == null ? [] : List<GetEitAttachmentList>.from(json["GetEITAttachmentList"]!.map((x) => GetEitAttachmentList.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"GetEITAttachmentList": getEitAttachmentList == null ? [] : List<dynamic>.from(getEitAttachmentList!.map((x) => x.toJson())),
};
}
class GetEitAttachmentList {
int? attachedDocumentId;
int? categoryId;
int? datatypeId;
int? documentId;
String? entityName;
String? fileContentType;
String? fileData;
int? fileId;
String? fileName;
String? pk1Value;
String? pk2Value;
String? pk3Value;
String? pk4Value;
String? pk5Value;
int? seqNum;
GetEitAttachmentList({
this.attachedDocumentId,
this.categoryId,
this.datatypeId,
this.documentId,
this.entityName,
this.fileContentType,
this.fileData,
this.fileId,
this.fileName,
this.pk1Value,
this.pk2Value,
this.pk3Value,
this.pk4Value,
this.pk5Value,
this.seqNum,
});
factory GetEitAttachmentList.fromRawJson(String str) => GetEitAttachmentList.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory GetEitAttachmentList.fromJson(Map<String, dynamic> json) => GetEitAttachmentList(
attachedDocumentId: json["ATTACHED_DOCUMENT_ID"],
categoryId: json["CATEGORY_ID"],
datatypeId: json["DATATYPE_ID"],
documentId: json["DOCUMENT_ID"],
entityName: json["ENTITY_NAME"],
fileContentType: json["FILE_CONTENT_TYPE"],
fileData: json["FILE_DATA"],
fileId: json["FILE_ID"],
fileName: json["FILE_NAME"],
pk1Value: json["PK1_VALUE"],
pk2Value: json["PK2_VALUE"],
pk3Value: json["PK3_VALUE"],
pk4Value: json["PK4_VALUE"],
pk5Value: json["PK5_VALUE"],
seqNum: json["SEQ_NUM"],
);
Map<String, dynamic> toJson() => {
"ATTACHED_DOCUMENT_ID": attachedDocumentId,
"CATEGORY_ID": categoryId,
"DATATYPE_ID": datatypeId,
"DOCUMENT_ID": documentId,
"ENTITY_NAME": entityName,
"FILE_CONTENT_TYPE": fileContentType,
"FILE_DATA": fileData,
"FILE_ID": fileId,
"FILE_NAME": fileName,
"PK1_VALUE": pk1Value,
"PK2_VALUE": pk2Value,
"PK3_VALUE": pk3Value,
"PK4_VALUE": pk4Value,
"PK5_VALUE": pk5Value,
"SEQ_NUM": seqNum,
};
}

@ -107,7 +107,6 @@ import 'package:mohem_flutter_app/models/vacation_rule/respond_attributes_list_m
import 'package:mohem_flutter_app/models/vacation_rule/vr_item_types_list_model.dart'; import 'package:mohem_flutter_app/models/vacation_rule/vr_item_types_list_model.dart';
import 'package:mohem_flutter_app/models/vacation_rule/wf_look_up_list_model.dart'; import 'package:mohem_flutter_app/models/vacation_rule/wf_look_up_list_model.dart';
import 'package:mohem_flutter_app/models/validate_eit_transaction_list_model.dart'; import 'package:mohem_flutter_app/models/validate_eit_transaction_list_model.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_category_model.dart';
import 'package:mohem_flutter_app/models/worklist/GetRFCEmployeeList.dart'; import 'package:mohem_flutter_app/models/worklist/GetRFCEmployeeList.dart';
import 'package:mohem_flutter_app/models/worklist/get_fad_notifications.dart'; import 'package:mohem_flutter_app/models/worklist/get_fad_notifications.dart';
import 'package:mohem_flutter_app/models/worklist/get_favorite_replacements_model.dart'; import 'package:mohem_flutter_app/models/worklist/get_favorite_replacements_model.dart';
@ -399,19 +398,7 @@ class GenericResponseModel {
bool? isActiveCode; bool? isActiveCode;
bool? isSMSSent; bool? isSMSSent;
PortalDirectionData? portalDirectionData; PortalDirectionData? portalDirectionData;
String? Mohemm_ENSAT_GetItgToken_Response;
AtlasInitiateChatResponse? atlasInitiateChatResponse;
AtlasContinueChatResponseDetails? atlasContinueChatResponseDetails;
GetWalaplusDepInfoModel? getWalaplusDepInfoModel;
dynamic getWalaplusDepInfoModelInternal;
List<WalaPlusCategoryModel>? walaPlusCategories;
String? wpFavoritesAddRemoveMessage;
String? businessCardQROutside;
EmpBusinessCardInfoModel? empBusinessCardInfoModel;
bool? isEmpLastLoginToDevice;
GenericResponseModel({ GenericResponseModel({
this.date, this.date,
this.languageID, this.languageID,
@ -682,18 +669,7 @@ class GenericResponseModel {
this.ePharmacyGetItemOnHandList, this.ePharmacyGetItemOnHandList,
this.isActiveCode, this.isActiveCode,
this.isSMSSent, this.isSMSSent,
this.getFADisposalNtfDetails, this.getFADisposalNtfDetails
this.Mohemm_ENSAT_GetItgToken_Response,
this.getWalaplusDepInfoModel,
this.getWalaplusDepInfoModelInternal,
this.walaPlusCategories,
this.wpFavoritesAddRemoveMessage,
this.businessCardQROutside,
this.empBusinessCardInfoModel,
this.isEmpLastLoginToDevice,
this.atlasInitiateChatResponse,
this.atlasContinueChatResponseDetails,
}); });
GenericResponseModel.fromJson(Map<String, dynamic> json) { GenericResponseModel.fromJson(Map<String, dynamic> json) {
@ -702,7 +678,7 @@ class GenericResponseModel {
serviceName = json['ServiceName']; serviceName = json['ServiceName'];
time = json['Time']; time = json['Time'];
androidLink = json['AndroidLink']; androidLink = json['AndroidLink'];
authenticationTokenID = json['AuthenticationTokenID']?.toString(); authenticationTokenID = json['AuthenticationTokenID'];
data = json['Data']; data = json['Data'];
dataw = json['Dataw']; dataw = json['Dataw'];
dietType = json['DietType']; dietType = json['DietType'];
@ -967,6 +943,7 @@ class GenericResponseModel {
getFADisposalNtfDetails = json['GetFADisposalNtfDetails'] != null ? GetFaDisposalNtfDetails.fromJson(json['GetFADisposalNtfDetails']) : null; getFADisposalNtfDetails = json['GetFADisposalNtfDetails'] != null ? GetFaDisposalNtfDetails.fromJson(json['GetFADisposalNtfDetails']) : null;
if (json['GetEarningsList'] != null) { if (json['GetEarningsList'] != null) {
getEarningsList = <GetEarningsList>[]; getEarningsList = <GetEarningsList>[];
json['GetEarningsList'].forEach((v) { json['GetEarningsList'].forEach((v) {
@ -1487,26 +1464,6 @@ class GenericResponseModel {
ePharmacyGetItemOnHandList = json['ePharmacy_GetItemOnHandList']; ePharmacyGetItemOnHandList = json['ePharmacy_GetItemOnHandList'];
isActiveCode = json['isActiveCode']; isActiveCode = json['isActiveCode'];
isSMSSent = json['isSMSSent']; isSMSSent = json['isSMSSent'];
atlasInitiateChatResponse = json["Atlas_InitiateChat_Response"] == null ? null : AtlasInitiateChatResponse.fromJson(json["Atlas_InitiateChat_Response"]);
atlasContinueChatResponseDetails = json["Atlas_ContinueChat_ResponseDetails"] == null ? null : AtlasContinueChatResponseDetails.fromJson(json["Atlas_ContinueChat_ResponseDetails"]);
Mohemm_ENSAT_GetItgToken_Response = json['Mohemm_ENSAT_GetItgToken_Response'];
getWalaplusDepInfoModel = json['GET_WALAPLUS_DEP_INFO_Model'] != null ? GetWalaplusDepInfoModel.fromJson(json['GET_WALAPLUS_DEP_INFO_Model']) : null;
getWalaplusDepInfoModelInternal = json['GET_WALAPLUS_DEP_INFO_Model_Internal'];
businessCardQROutside = json['BusinessCardQR_Outside'];
empBusinessCardInfoModel = json['GET_EMP_BUSINESS_CARD_INFO_Model'] != null ? EmpBusinessCardInfoModel.fromJson(json['GET_EMP_BUSINESS_CARD_INFO_Model']) : null;
if (json['WalaPlus_Categories'] != null) {
walaPlusCategories = <WalaPlusCategoryModel>[];
json['WalaPlus_Categories'].forEach((v) {
walaPlusCategories!.add(WalaPlusCategoryModel.fromJson(v));
});
}
wpFavoritesAddRemoveMessage = json['WP_Favorites_AddRemove_Message'] ?? json['wp_Favorites_AddRemove_Message'];
isEmpLastLoginToDevice = json["IsEmpLastLoginToDevice"];
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@ -1604,7 +1561,7 @@ class GenericResponseModel {
if (this.getActionHistoryList != null) { if (this.getActionHistoryList != null) {
data['GetActionHistoryList'] = this.getActionHistoryList!.map((v) => v.toJson()).toList(); data['GetActionHistoryList'] = this.getActionHistoryList!.map((v) => v.toJson()).toList();
} }
data['GetFADisposalNtfDetails'] = this.getFADisposalNtfDetails; data['GetFADisposalNtfDetails'] =this.getFADisposalNtfDetails;
data['GetAddressDffStructureList'] = this.getAddressDffStructureList; data['GetAddressDffStructureList'] = this.getAddressDffStructureList;
data['GetAddressNotificationBodyList'] = this.getAddressNotificationBodyList; data['GetAddressNotificationBodyList'] = this.getAddressNotificationBodyList;
@ -1992,28 +1949,6 @@ class GenericResponseModel {
data['ePharmacy_GetItemOnHandList'] = this.ePharmacyGetItemOnHandList; data['ePharmacy_GetItemOnHandList'] = this.ePharmacyGetItemOnHandList;
data['isActiveCode'] = this.isActiveCode; data['isActiveCode'] = this.isActiveCode;
data['isSMSSent'] = this.isSMSSent; data['isSMSSent'] = this.isSMSSent;
data["Atlas_InitiateChat_Response"] = atlasInitiateChatResponse?.toJson();
data["Atlas_ContinueChat_ResponseDetails"] = atlasContinueChatResponseDetails?.toJson();
data['Mohemm_ENSAT_GetItgToken_Response'] = this.Mohemm_ENSAT_GetItgToken_Response;
data['GET_WALAPLUS_DEP_INFO_Model_Internal'] = this.getWalaplusDepInfoModelInternal;
if (this.getWalaplusDepInfoModel != null) {
data['GET_WALAPLUS_DEP_INFO_Model'] = this.getWalaplusDepInfoModel!.toJson();
}
data['BusinessCardQR_Outside'] = this.businessCardQROutside;
if (this.empBusinessCardInfoModel != null) {
data['GET_EMP_BUSINESS_CARD_INFO_Model'] = this.empBusinessCardInfoModel!.toJson();
}
if (walaPlusCategories != null) {
data['WalaPlus_Categories'] = walaPlusCategories!.map((WalaPlusCategoryModel v) => v.toJson()).toList();
}
if (this.wpFavoritesAddRemoveMessage != null) {
data['WP_Favorites_AddRemove_Message'] = this.wpFavoritesAddRemoveMessage;
}
data["IsEmpLastLoginToDevice"] = this.isEmpLastLoginToDevice;
return data; return data;
} }
} }
@ -2039,182 +1974,3 @@ class TicketBookingResult {
TicketBookingResult(this.success, this.clientId); TicketBookingResult(this.success, this.clientId);
} }
class GetWalaplusDepInfoModel {
String? pEmpEmailAddress;
String? pEmpMobileNumber;
String? pDependentName;
GetWalaplusDepInfoModel({this.pEmpEmailAddress, this.pEmpMobileNumber, this.pDependentName});
factory GetWalaplusDepInfoModel.fromJson(Map<String, dynamic> json) =>
GetWalaplusDepInfoModel(pEmpEmailAddress: json["P_EMP_EMAIL_ADDRESS"], pEmpMobileNumber: json["P_EMP_MOBILE_NUMBER"], pDependentName: json["P_DEPENDENT_NAME"]);
Map<String, dynamic> toJson() => {"P_EMP_EMAIL_ADDRESS": pEmpEmailAddress, "P_EMP_MOBILE_NUMBER": pEmpMobileNumber, "P_DEPENDENT_NAME": pDependentName};
}
class EmpBusinessCardInfoModel {
String? pEmailAddress;
String? pFirstName;
String? pLastName;
String? pMobilePhoneNumber;
String? pPositionTitle;
String? pWorkExtensionNumber;
EmpBusinessCardInfoModel({this.pEmailAddress, this.pFirstName, this.pLastName, this.pMobilePhoneNumber, this.pPositionTitle, this.pWorkExtensionNumber});
String get fullName => [pFirstName, pLastName].where((e) => e != null && e.trim().isNotEmpty).join(" ");
factory EmpBusinessCardInfoModel.fromJson(Map<String, dynamic> json) => EmpBusinessCardInfoModel(
pEmailAddress: json["P_EMAIL_ADDRESS"],
pFirstName: json["P_FIRST_NAME"],
pLastName: json["P_LAST_NAME"],
pMobilePhoneNumber: json["P_MOBILE_PHONE_NUMBER"],
pPositionTitle: json["P_POSITION_TITLE"],
pWorkExtensionNumber: json["P_WORK_EXTENSION_NUMBER"],
);
Map<String, dynamic> toJson() => {
"P_EMAIL_ADDRESS": pEmailAddress,
"P_FIRST_NAME": pFirstName,
"P_LAST_NAME": pLastName,
"P_MOBILE_PHONE_NUMBER": pMobilePhoneNumber,
"P_POSITION_TITLE": pPositionTitle,
"P_WORK_EXTENSION_NUMBER": pWorkExtensionNumber,
};
}
class AtlasInitiateChatResponse {
String? atlasAccessToken;
String? atlasChatSessionId;
String? atlasTokenType;
AtlasInitiateChatResponse({this.atlasAccessToken, this.atlasChatSessionId, this.atlasTokenType});
factory AtlasInitiateChatResponse.fromRawJson(String str) => AtlasInitiateChatResponse.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory AtlasInitiateChatResponse.fromJson(Map<String, dynamic> json) =>
AtlasInitiateChatResponse(atlasAccessToken: json["Atlas_AccessToken"], atlasChatSessionId: json["Atlas_ChatSessionID"], atlasTokenType: json["Atlas_TokenType"]);
Map<String, dynamic> toJson() => {"Atlas_AccessToken": atlasAccessToken, "Atlas_ChatSessionID": atlasChatSessionId, "Atlas_TokenType": atlasTokenType};
}
class AtlasContinueChatResponseDetails {
AiResponse? aiResponse;
QueryClassification? queryClassification;
String? sessionId;
AiResponse? userMessage;
AtlasContinueChatResponseDetails({
this.aiResponse,
this.queryClassification,
this.sessionId,
this.userMessage,
});
factory AtlasContinueChatResponseDetails.fromRawJson(String str) => AtlasContinueChatResponseDetails.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory AtlasContinueChatResponseDetails.fromJson(Map<String, dynamic> json) => AtlasContinueChatResponseDetails(
aiResponse: json["ai_response"] == null ? null : AiResponse.fromJson(json["ai_response"]),
queryClassification: json["query_classification"] == null ? null : QueryClassification.fromJson(json["query_classification"]),
sessionId: json["session_id"],
userMessage: json["user_message"] == null ? null : AiResponse.fromJson(json["user_message"]),
);
Map<String, dynamic> toJson() => {
"ai_response": aiResponse?.toJson(),
"query_classification": queryClassification?.toJson(),
"session_id": sessionId,
"user_message": userMessage?.toJson(),
};
}
class AiResponse {
DateTime? createdAt;
String? id;
StructuredData? structuredData;
String? text;
AiResponse({
this.createdAt,
this.id,
this.structuredData,
this.text,
});
factory AiResponse.fromRawJson(String str) => AiResponse.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory AiResponse.fromJson(Map<String, dynamic> json) => AiResponse(
createdAt: json["created_at"] == null ? null : DateTime.parse(json["created_at"]),
id: json["id"],
structuredData: json["structured_data"] == null ? null : StructuredData.fromJson(json["structured_data"]),
text: json["text"],
);
Map<String, dynamic> toJson() => {
"created_at": createdAt?.toIso8601String(),
"id": id,
"structured_data": structuredData?.toJson(),
"text": text,
};
}
class StructuredData {
String? answer;
List<dynamic>? sources;
String? type;
StructuredData({
this.answer,
this.sources,
this.type,
});
factory StructuredData.fromRawJson(String str) => StructuredData.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory StructuredData.fromJson(Map<String, dynamic> json) => StructuredData(
answer: json["answer"],
sources: json["sources"] == null ? [] : List<dynamic>.from(json["sources"]!.map((x) => x)),
type: json["type"],
);
Map<String, dynamic> toJson() => {
"answer": answer,
"sources": sources == null ? [] : List<dynamic>.from(sources!.map((x) => x)),
"type": type,
};
}
class QueryClassification {
int? confidence;
String? type;
QueryClassification({
this.confidence,
this.type,
});
factory QueryClassification.fromRawJson(String str) => QueryClassification.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory QueryClassification.fromJson(Map<String, dynamic> json) => QueryClassification(
confidence: json["confidence"],
type: json["type"],
);
Map<String, dynamic> toJson() => {
"confidence": confidence,
"type": type,
};
}

@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart';
class GetEmployeeContactsList { class GetEmployeeContactsList {
String? cONTACTNAME; String? cONTACTNAME;
@ -9,34 +8,16 @@ class GetEmployeeContactsList {
int? pERSONID; int? pERSONID;
String? pRIMARYCONTACTFLAG; String? pRIMARYCONTACTFLAG;
String? rELATIONSHIP; String? rELATIONSHIP;
String? mAZAYAACCESSFLG;
dynamic mazayaAccess;
String? nATIONALIDENTIFIER;
// Real ERP response fields - needed to call the grant-access API later (as P_BIRTH_DATE / GetEmployeeContactsList(
// P_NHIC_VERIFIED_FLAG request params). PatientIdentificationID/PatientOutSA are NOT {this.cONTACTNAME,
// response fields - they're constructed at call time (from nATIONALIDENTIFIER, and a
// hardcoded 0, respectively), so they don't belong on this model.
// Not a plain string per ERP - keep untyped so whatever shape comes back (epoch, nested
// date object, etc.) survives round-trip, instead of assuming a format.
dynamic pBIRTHDATE;
String? pNHICVERIFIEDFLAG;
GetEmployeeContactsList({
this.cONTACTNAME,
this.cONTACTPERSONID, this.cONTACTPERSONID,
this.cONTACTRELATIONSHIPID, this.cONTACTRELATIONSHIPID,
this.cONTACTTYPE, this.cONTACTTYPE,
this.dATEOFBIRTH, this.dATEOFBIRTH,
this.pERSONID, this.pERSONID,
this.pRIMARYCONTACTFLAG, this.pRIMARYCONTACTFLAG,
this.rELATIONSHIP, this.rELATIONSHIP});
this.mAZAYAACCESSFLG,
this.mazayaAccess,
this.nATIONALIDENTIFIER,
this.pBIRTHDATE,
this.pNHICVERIFIEDFLAG,
});
GetEmployeeContactsList.fromJson(Map<String, dynamic> json) { GetEmployeeContactsList.fromJson(Map<String, dynamic> json) {
cONTACTNAME = json['CONTACT_NAME']; cONTACTNAME = json['CONTACT_NAME'];
@ -47,41 +28,18 @@ class GetEmployeeContactsList {
pERSONID = json['PERSON_ID']; pERSONID = json['PERSON_ID'];
pRIMARYCONTACTFLAG = json['PRIMARY_CONTACT_FLAG']; pRIMARYCONTACTFLAG = json['PRIMARY_CONTACT_FLAG'];
rELATIONSHIP = json['RELATIONSHIP']; rELATIONSHIP = json['RELATIONSHIP'];
mAZAYAACCESSFLG = json['MAZAYA_ACCESS_FLG']?.toString() ?? json['MAZAYA_ACCESS']?.toString();
mazayaAccess = mAZAYAACCESSFLG;
nATIONALIDENTIFIER =
json['NATIONAL_IDENTIFIER']?.toString() ??
json['NATIONAL_ID']?.toString() ??
json['CIVIL_ID']?.toString() ??
json['IQAMA_NUMBER']?.toString() ??
json['CONTACT_NATIONAL_IDENTIFIER']?.toString() ??
json['CONTACT_NATIONAL_ID']?.toString() ??
json['CONTACT_IDENTIFICATION_ID']?.toString() ??
json['IDENTIFICATION_ID']?.toString() ??
json['NATIONALID']?.toString();
if (nATIONALIDENTIFIER == null || nATIONALIDENTIFIER!.trim().isEmpty) {
debugPrint("GetEmployeeContactsList: no national id match found, raw contact json: $json");
}
pBIRTHDATE = json['P_BIRTH_DATE'];
pNHICVERIFIEDFLAG = json['P_NHIC_VERIFIED_FLAG']?.toString();
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{}; Map<String, dynamic> data = new Map<String, dynamic>();
data['CONTACT_NAME'] = cONTACTNAME; data['CONTACT_NAME'] = this.cONTACTNAME;
data['CONTACT_PERSON_ID'] = cONTACTPERSONID; data['CONTACT_PERSON_ID'] = this.cONTACTPERSONID;
data['CONTACT_RELATIONSHIP_ID'] = cONTACTRELATIONSHIPID; data['CONTACT_RELATIONSHIP_ID'] = this.cONTACTRELATIONSHIPID;
data['CONTACT_TYPE'] = cONTACTTYPE; data['CONTACT_TYPE'] = this.cONTACTTYPE;
data['DATE_OF_BIRTH'] = dATEOFBIRTH; data['DATE_OF_BIRTH'] = this.dATEOFBIRTH;
data['PERSON_ID'] = pERSONID; data['PERSON_ID'] = this.pERSONID;
data['PRIMARY_CONTACT_FLAG'] = pRIMARYCONTACTFLAG; data['PRIMARY_CONTACT_FLAG'] = this.pRIMARYCONTACTFLAG;
data['RELATIONSHIP'] = rELATIONSHIP; data['RELATIONSHIP'] = this.rELATIONSHIP;
data['MAZAYA_ACCESS_FLG'] = mAZAYAACCESSFLG;
data['MAZAYA_ACCESS'] = mazayaAccess;
data['NATIONAL_IDENTIFIER'] = nATIONALIDENTIFIER;
data['P_BIRTH_DATE'] = pBIRTHDATE;
data['P_NHIC_VERIFIED_FLAG'] = pNHICVERIFIEDFLAG;
return data; return data;
} }
} }

@ -1,6 +1,6 @@
class GetMobileLoginInfoListModel { class GetMobileLoginInfoListModel {
int? iD; int? iD;
dynamic? employeeID; //do not change this int? employeeID;
int? channelID; int? channelID;
int? companyID; int? companyID;
String? deviceType; String? deviceType;

@ -5,13 +5,12 @@ class GetPrNotificationBodyList {
String? pINFORMATION; String? pINFORMATION;
String? pQUESTION; String? pQUESTION;
GetPrNotificationBodyList({ GetPrNotificationBodyList(
this.pRHeader, {this.pRHeader,
this.pRLines, this.pRLines,
this.pCURRENCYCODE, this.pCURRENCYCODE,
this.pINFORMATION, this.pINFORMATION,
this.pQUESTION, this.pQUESTION});
});
GetPrNotificationBodyList.fromJson(Map<String, dynamic> json) { GetPrNotificationBodyList.fromJson(Map<String, dynamic> json) {
if (json['PRHeader'] != null) { if (json['PRHeader'] != null) {
@ -79,12 +78,9 @@ class PRLines {
int? tOROWNUM; int? tOROWNUM;
num? uNITPRICE; num? uNITPRICE;
String? uOM; String? uOM;
String? dESTINATIONTYPE;
String? sOURCEORGANIZATIONNAME;
String? dESTINATIONORGANIZATIONNAME;
PRLines({ PRLines(
this.cOSTCENTER, {this.cOSTCENTER,
this.dESCRIPTION, this.dESCRIPTION,
this.fROMROWNUM, this.fROMROWNUM,
this.iTEMAMU, this.iTEMAMU,
@ -96,11 +92,7 @@ class PRLines {
this.rOWNUM, this.rOWNUM,
this.tOROWNUM, this.tOROWNUM,
this.uNITPRICE, this.uNITPRICE,
this.uOM, this.uOM});
this.dESTINATIONTYPE,
this.sOURCEORGANIZATIONNAME,
this.dESTINATIONORGANIZATIONNAME,
});
PRLines.fromJson(Map<String, dynamic> json) { PRLines.fromJson(Map<String, dynamic> json) {
cOSTCENTER = json['COST_CENTER']; cOSTCENTER = json['COST_CENTER'];
@ -116,9 +108,6 @@ class PRLines {
tOROWNUM = json['TO_ROW_NUM']; tOROWNUM = json['TO_ROW_NUM'];
uNITPRICE = json['UNIT_PRICE']; uNITPRICE = json['UNIT_PRICE'];
uOM = json['UOM']; uOM = json['UOM'];
dESTINATIONTYPE = json['DESTINATION_TYPE'];
sOURCEORGANIZATIONNAME = json['SOURCE_ORGANIZATION_NAME'];
dESTINATIONORGANIZATIONNAME = json['DESTINATION_ORGANIZATION_NAME'];
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@ -136,9 +125,6 @@ class PRLines {
data['TO_ROW_NUM'] = this.tOROWNUM; data['TO_ROW_NUM'] = this.tOROWNUM;
data['UNIT_PRICE'] = this.uNITPRICE; data['UNIT_PRICE'] = this.uNITPRICE;
data['UOM'] = this.uOM; data['UOM'] = this.uOM;
data['DESTINATION_TYPE'] = this.dESTINATIONTYPE;
data['SOURCE_ORGANIZATION_NAME'] = this.sOURCEORGANIZATIONNAME;
data['DESTINATION_ORGANIZATION_NAME'] = this.dESTINATIONORGANIZATIONNAME;
return data; return data;
} }
} }

@ -12,7 +12,12 @@ MohemmItgResponseItem mohemmItgResponseItemFromJson(String str) => MohemmItgResp
String mohemmItgResponseItemToJson(MohemmItgResponseItem data) => json.encode(data.toJson()); String mohemmItgResponseItemToJson(MohemmItgResponseItem data) => json.encode(data.toJson());
class MohemmItgResponseItem { class MohemmItgResponseItem {
MohemmItgResponseItem({this.statusCode, this.message, this.originalErrMsg, this.result}); MohemmItgResponseItem({
this.statusCode,
this.message,
this.originalErrMsg,
this.result,
});
final int? statusCode; final int? statusCode;
final dynamic? message; final dynamic? message;
@ -26,58 +31,71 @@ class MohemmItgResponseItem {
result: json["result"] == null ? null : ItgResponseResult.fromJson(json["result"]), result: json["result"] == null ? null : ItgResponseResult.fromJson(json["result"]),
); );
Map<String, dynamic> toJson() => {"statusCode": statusCode == null ? null : statusCode, "message": message, "originalErrMsg": originalErrMsg, "result": result == null ? null : result!.toJson()}; Map<String, dynamic> toJson() => {
"statusCode": statusCode == null ? null : statusCode,
"message": message,
"originalErrMsg": originalErrMsg,
"result": result == null ? null : result!.toJson(),
};
} }
class ItgResponseResult { class ItgResponseResult {
ItgResponseResult({this.totalItemsCount, this.data, this.errormsg}); ItgResponseResult({
this.totalItemsCount,
// this.data,
// final dynamic totalItemsCount; this.errormsg,
// final ItgResponseData? data; });
// final dynamic errormsg; //
// // final dynamic totalItemsCount;
// factory ItgResponseResult.fromJson(Map<String, dynamic> json) => ItgResponseResult( // final ItgResponseData? data;
// totalItemsCount: json["totalItemsCount"], // final dynamic errormsg;
// data: json["data"] == null ? null : ItgResponseData.fromJson(json["data"]), //
// errormsg: json["errormsg"], // factory ItgResponseResult.fromJson(Map<String, dynamic> json) => ItgResponseResult(
// ); // totalItemsCount: json["totalItemsCount"],
// // data: json["data"] == null ? null : ItgResponseData.fromJson(json["data"]),
// Map<String, dynamic> toJson() => { // errormsg: json["errormsg"],
// "totalItemsCount": totalItemsCount, // );
// "data": data == null ? null : data!.toJson(), //
// "errormsg": errormsg, // Map<String, dynamic> toJson() => {
// }; // "totalItemsCount": totalItemsCount,
// } // "data": data == null ? null : data!.toJson(),
// "errormsg": errormsg,
// todo these lines, for ERM new change on UAT // };
dynamic totalItemsCount; // }
List<ItgResponseData>? data;
dynamic errormsg; // todo these lines, for ERM new change on UAT
dynamic totalItemsCount;
ItgResponseResult.fromJson(Map<String, dynamic> json) { List<ItgResponseData>? data;
dynamic errormsg;
ItgResponseResult.fromJson(Map<String, dynamic> json) {
totalItemsCount = json['totalItemsCount']; totalItemsCount = json['totalItemsCount'];
if (json['data'] != null) { if (json['data'] != null) {
data = []; data = [];
if (json['data'] is List) { if(json['data'] is List) {
json['data'].forEach((v) { json['data'].forEach((v) {
data!.add(ItgResponseData.fromJson(v)); data!.add(ItgResponseData.fromJson(v));
}); });
} else { } else {
data!.add(ItgResponseData.fromJson(json['data'])); data!.add(ItgResponseData.fromJson(json['data']));
} }
} }
errormsg = json['errormsg']; errormsg = json['errormsg'];
} }
//
// factory ItgResponseResult.fromJson(Map<String, dynamic> json) => ItgResponseResult(
// totalItemsCount: json["totalItemsCount"],
// data: json["data"] == null ? null : ItgResponseData.fromJson(json["data"]),
// errormsg: json["errormsg"],
// );
Map<String, dynamic> toJson() => {"totalItemsCount": totalItemsCount, "data": data == null ? null : data!.map((v) => v.toJson()).toList(), "errormsg": errormsg}; //
// factory ItgResponseResult.fromJson(Map<String, dynamic> json) => ItgResponseResult(
// totalItemsCount: json["totalItemsCount"],
// data: json["data"] == null ? null : ItgResponseData.fromJson(json["data"]),
// errormsg: json["errormsg"],
// );
Map<String, dynamic> toJson() => {
"totalItemsCount": totalItemsCount,
"data": data == null ? null : data!.map((v) => v.toJson()).toList(),
"errormsg": errormsg,
};
} }
class ItgResponseData { class ItgResponseData {
@ -103,7 +121,6 @@ class ItgResponseData {
this.pageSize, this.pageSize,
this.pageNo, this.pageNo,
this.languageId, this.languageId,
this.deadline,
}); });
final String? notificationMasterId; final String? notificationMasterId;
@ -127,7 +144,6 @@ class ItgResponseData {
final dynamic pageSize; final dynamic pageSize;
final dynamic pageNo; final dynamic pageNo;
final dynamic languageId; final dynamic languageId;
dynamic deadline;
factory ItgResponseData.fromJson(Map<String, dynamic> json) => ItgResponseData( factory ItgResponseData.fromJson(Map<String, dynamic> json) => ItgResponseData(
notificationMasterId: json["notificationMasterId"] == null ? null : json["notificationMasterId"], notificationMasterId: json["notificationMasterId"] == null ? null : json["notificationMasterId"],
@ -138,7 +154,6 @@ class ItgResponseData {
applicationItemId: json["applicationItemId"], applicationItemId: json["applicationItemId"],
startDate: json["startDate"], startDate: json["startDate"],
endDate: json["endDate"], endDate: json["endDate"],
deadline: json["deadline"] == null ? null : json["deadline"],
isRepeat: json["isRepeat"] == null ? null : json["isRepeat"], isRepeat: json["isRepeat"] == null ? null : json["isRepeat"],
channelId: json["channelId"] == null ? null : json["channelId"], channelId: json["channelId"] == null ? null : json["channelId"],
serviceId: json["serviceId"] == null ? null : json["serviceId"], serviceId: json["serviceId"] == null ? null : json["serviceId"],
@ -163,7 +178,6 @@ class ItgResponseData {
"applicationItemId": applicationItemId, "applicationItemId": applicationItemId,
"startDate": startDate, "startDate": startDate,
"endDate": endDate, "endDate": endDate,
"deadline": deadline == null ? null : deadline ,
"isRepeat": isRepeat == null ? null : isRepeat, "isRepeat": isRepeat == null ? null : isRepeat,
"channelId": channelId == null ? null : channelId, "channelId": channelId == null ? null : channelId,
"serviceId": serviceId == null ? null : serviceId, "serviceId": serviceId == null ? null : serviceId,

@ -1,75 +0,0 @@
class WalaPlusCategoryModel {
String? id;
String? image;
String? imageThumb;
String? name;
String? parentId;
String? countryNameAr;
String? countryNameEn;
WalaPlusCategoryModel({
this.id,
this.image,
this.imageThumb,
this.name,
this.parentId,
this.countryNameAr,
this.countryNameEn,
}) {
if ((image == null || image!.isEmpty) && id != null && id!.trim().isNotEmpty && id != "all" && id != "0") {
String cleanId = id!.trim();
if (cleanId.toLowerCase().endsWith('.png')) {
cleanId = cleanId.substring(0, cleanId.length - 4);
}
image = Uri.encodeFull("https://epharmacy.hmg.com/middleware/images/category/$cleanId.png");
}
}
WalaPlusCategoryModel.fromJson(Map<String, dynamic> json) {
id = (json['id'] ?? json['Id'] ?? json['ID'])?.toString();
name = (json['name'] ?? json['Name'] ?? json['category_name'] ?? json['categoryName'] ?? json['cat_name'] ?? json['CategoryName'])?.toString();
parentId = (json['parent_id'] ?? json['Parent_Id'] ?? json['parentId'])?.toString();
imageThumb = (json['image_thumb'] ?? json['Image_Thumb'] ?? json['imageThumb'])?.toString();
countryNameAr = (json['country_name_ar'] ?? json['countryNameAr'] ?? json['category_name_ar'] ?? json['categoryNameAr'] ?? json['cat_name_ar'] ?? json['name_ar'])?.toString();
countryNameEn = (json['country_name_en'] ?? json['countryNameEn'] ?? json['category_name_en'] ?? json['categoryNameEn'] ?? json['cat_name_en'] ?? json['name_en'])?.toString();
// Use category image URL from HMG middleware using ID from WalaPlus_Categories API:
// https://epharmacy.hmg.com/middleware/images/category/{id}.png
if (id != null && id!.trim().isNotEmpty && id != "all" && id != "0") {
String cleanId = id!.trim();
if (cleanId.toLowerCase().endsWith('.png')) {
cleanId = cleanId.substring(0, cleanId.length - 4);
}
image = Uri.encodeFull("https://epharmacy.hmg.com/middleware/images/category/$cleanId.png");
} else {
image = (json['image'] ?? json['Image'])?.toString();
}
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['image'] = image;
data['image_thumb'] = imageThumb;
data['name'] = name;
data['parent_id'] = parentId;
data['country_name_ar'] = countryNameAr;
data['country_name_en'] = countryNameEn;
return data;
}
bool get isMainCategory => parentId == null || parentId!.isEmpty;
bool get isSvg => (image ?? '').toLowerCase().contains('.svg');
String get categoryImageUrl {
if (id != null && id!.trim().isNotEmpty && id != "all" && id != "0") {
String cleanId = id!.trim();
if (cleanId.toLowerCase().endsWith('.png')) {
cleanId = cleanId.substring(0, cleanId.length - 4);
}
return Uri.encodeFull("https://epharmacy.hmg.com/middleware/images/category/$cleanId.png");
}
return image ?? '';
}
}

@ -1,51 +0,0 @@
class WalaPlusCityModel {
int? cityId;
String? cityNameAr;
String? cityNameEn;
String? countryNameAr;
String? countryNameEn;
WalaPlusCityModel({
this.cityId,
this.cityNameAr,
this.cityNameEn,
this.countryNameAr,
this.countryNameEn,
});
factory WalaPlusCityModel.fromJson(Map<String, dynamic> json) {
return WalaPlusCityModel(
cityId:
json['city_id'] is int
? json['city_id']
: int.tryParse(json['city_id']?.toString() ?? ''),
cityNameAr: json['city_name_ar']?.toString(),
cityNameEn: json['city_name_en']?.toString(),
countryNameAr: json['country_name_ar']?.toString(),
countryNameEn: json['country_name_en']?.toString(),
);
}
Map<String, dynamic> toJson() {
return {
'city_id': cityId,
'city_name_ar': cityNameAr,
'city_name_en': cityNameEn,
'country_name_ar': countryNameAr,
'country_name_en': countryNameEn,
};
}
String getDisplayName(bool isAr) {
String? primary = isAr ? cityNameAr : cityNameEn;
if (primary != null && primary.trim().isNotEmpty) return primary.trim();
String? fallback = isAr ? cityNameEn : cityNameAr;
return fallback?.trim() ?? '';
}
bool get isAllCities {
String en = (cityNameEn ?? '').trim().toLowerCase();
String ar = (cityNameAr ?? '').trim();
return en == 'all cities' || ar == 'جميع المدن';
}
}

@ -1,3 +1,5 @@
import 'package:mohem_flutter_app/app_state/app_state.dart';
class PostParamsModel { class PostParamsModel {
double? versionID; double? versionID;
int? channel; int? channel;
@ -13,7 +15,6 @@ class PostParamsModel {
String? payrollCodeStr; String? payrollCodeStr;
int? pSessionId; int? pSessionId;
String? userName; String? userName;
String? sMSSignature;
PostParamsModel({ PostParamsModel({
this.versionID, this.versionID,
@ -30,7 +31,6 @@ class PostParamsModel {
this.pSelectedEmployeeNumber, this.pSelectedEmployeeNumber,
this.payrollCodeStr, this.payrollCodeStr,
this.pLegislationCode, this.pLegislationCode,
this.sMSSignature,
}); });
PostParamsModel.fromJson(Map<String, dynamic> json) { PostParamsModel.fromJson(Map<String, dynamic> json) {
@ -40,7 +40,7 @@ class PostParamsModel {
mobileType = json['MobileType']; mobileType = json['MobileType'];
logInTokenID = json['LogInTokenID']; logInTokenID = json['LogInTokenID'];
payrollCodeStr = json['PayrollCodeStr']; payrollCodeStr = json['PayrollCodeStr'];
pLegislationCode = json['LegislationCodeStr']?.toString().trim().toUpperCase(); pLegislationCode = json['LegislationCodeStr'];
tokenID = json['TokenID']; tokenID = json['TokenID'];
} }
@ -92,9 +92,7 @@ class PostParamsModel {
set setPSelectedEmployeeNumber(String? v) => pSelectedEmployeeNumber = v; set setPSelectedEmployeeNumber(String? v) => pSelectedEmployeeNumber = v;
set setPLegislationCode(String? v) => pLegislationCode = v?.trim().toUpperCase(); set setPLegislationCode(String? v) => pLegislationCode = v;
set setPayrollCodeStr(String? v) => payrollCodeStr = v; set setPayrollCodeStr(String? v) => payrollCodeStr = v;
set setSMSSignature(String? v) => sMSSignature = v;
set setSmsSignature(String? v) => sMSSignature = v;
} }

@ -7,23 +7,20 @@ class PrivilegeListModel {
int? iD; int? iD;
String? serviceName; String? serviceName;
bool? previlege; bool? previlege;
bool? previlegeHMG;
PrivilegeListModel({this.iD, this.serviceName, this.previlege, this.previlegeHMG}); PrivilegeListModel({this.iD, this.serviceName, this.previlege});
PrivilegeListModel.fromJson(Map<String, dynamic> json) { PrivilegeListModel.fromJson(Map<String, dynamic> json) {
iD = json['ID']; iD = json['ID'];
serviceName = json['ServiceName']; serviceName = json['ServiceName'];
previlege = json['Previlege'] ?? json['previlege'] ?? json['Privilege'] ?? json['privilege']; previlege = json['Previlege'];
previlegeHMG = json['Previlege_HMG'] ?? json['PrevilegeHMG'] ?? json['previlege_HMG'] ?? json['previlegeHMG'] ?? json['Privilege_HMG'] ?? json['PrivilegeHMG'];
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{}; Map<String, dynamic> data = new Map<String, dynamic>();
data['ID'] = iD; data['ID'] = this.iD;
data['ServiceName'] = serviceName; data['ServiceName'] = this.serviceName;
data['Previlege'] = previlege; data['Previlege'] = this.previlege;
data['Previlege_HMG'] = previlegeHMG;
return data; return data;
} }

@ -1,139 +0,0 @@
class PatientInfoModel {
int? age;
String? dateOfBirth;
String? firstNameAr;
String? firstNameEn;
String? secondNameAr;
String? secondNameEn;
String? thirdNameAr;
String? thirdNameEn;
String? lastNameAr;
String? lastNameEn;
String? gender;
String? healthId;
String? idNumber;
String? idType;
bool? isHijri;
String? maritalStatus;
String? maritalStatusCode;
String? nationalDateOfBirth;
String? nationality;
String? nationalityCode;
String? occupation;
String? patientStatus;
String? placeOfBirth;
int? messageStatus;
String? errorEndUserMessage;
String? errorMessage;
PatientInfoModel({
this.age,
this.dateOfBirth,
this.firstNameAr,
this.firstNameEn,
this.secondNameAr,
this.secondNameEn,
this.thirdNameAr,
this.thirdNameEn,
this.lastNameAr,
this.lastNameEn,
this.gender,
this.healthId,
this.idNumber,
this.idType,
this.isHijri,
this.maritalStatus,
this.maritalStatusCode,
this.nationalDateOfBirth,
this.nationality,
this.nationalityCode,
this.occupation,
this.patientStatus,
this.placeOfBirth,
this.messageStatus,
this.errorEndUserMessage,
this.errorMessage,
});
factory PatientInfoModel.fromJson(Map<String, dynamic> json) {
return PatientInfoModel(
age: json['Age'],
dateOfBirth: json['DateOfBirth'],
firstNameAr: json['FirstNameAr'],
firstNameEn: json['FirstNameEn'],
secondNameAr: json['SecondNameAr'],
secondNameEn: json['SecondNameEn'],
thirdNameAr: json['ThirdNameAr'],
thirdNameEn: json['ThirdNameEn'],
lastNameAr: json['LastNameAr'],
lastNameEn: json['LastNameEn'],
gender: json['Gender'],
healthId: json['HealthId'],
idNumber: json['IdNumber'],
idType: json['IdType'],
isHijri: json['IsHijri'],
maritalStatus: json['MaritalStatus'],
maritalStatusCode: json['MaritalStatusCode'],
nationalDateOfBirth: json['NationalDateOfBirth'],
nationality: json['Nationality'],
nationalityCode: json['NationalityCode'],
occupation: json['Occupation'],
patientStatus: json['PatientStatus']?.toString(),
placeOfBirth: json['PlaceofBirth'],
messageStatus: json['MessageStatus'],
errorEndUserMessage: json['ErrorEndUserMessage'],
errorMessage: json['ErrorMessage'],
);
}
Map<String, dynamic> toJson() {
return {
'Age': age,
'DateOfBirth': dateOfBirth,
'FirstNameAr': firstNameAr,
'FirstNameEn': firstNameEn,
'SecondNameAr': secondNameAr,
'SecondNameEn': secondNameEn,
'ThirdNameAr': thirdNameAr,
'ThirdNameEn': thirdNameEn,
'LastNameAr': lastNameAr,
'LastNameEn': lastNameEn,
'Gender': gender,
'HealthId': healthId,
'IdNumber': idNumber,
'IdType': idType,
'IsHijri': isHijri,
'MaritalStatus': maritalStatus,
'MaritalStatusCode': maritalStatusCode,
'NationalDateOfBirth': nationalDateOfBirth,
'Nationality': nationality,
'NationalityCode': nationalityCode,
'Occupation': occupation,
'PatientStatus': patientStatus,
'PlaceofBirth': placeOfBirth,
'MessageStatus': messageStatus,
'ErrorEndUserMessage': errorEndUserMessage,
'ErrorMessage': errorMessage,
};
}
String get fullNameAr {
List<String> parts = [
firstNameAr ?? '',
secondNameAr ?? '',
(thirdNameAr != null && thirdNameAr != '-') ? thirdNameAr! : '',
lastNameAr ?? '',
].where((s) => s.trim().isNotEmpty && s != '-').toList();
return parts.join(' ').trim();
}
String get fullNameEn {
List<String> parts = [
firstNameEn ?? '',
secondNameEn ?? '',
(thirdNameEn != null && thirdNameEn != '-') ? thirdNameEn! : '',
lastNameEn ?? '',
].where((s) => s.trim().isNotEmpty && s != '-').toList();
return parts.join(' ').trim();
}
}

@ -17,12 +17,12 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
MediaStream? _remoteStream; MediaStream? _remoteStream;
void initCallListeners() { void initCallListeners() {
chatHubConnection?.on("OnCallAcceptedAsync", onCallAcceptedAsync); chatHubConnection.on("OnCallAcceptedAsync", onCallAcceptedAsync);
chatHubConnection?.on("OnIceCandidateAsync", onIceCandidateAsync); chatHubConnection.on("OnIceCandidateAsync", onIceCandidateAsync);
chatHubConnection?.on("OnOfferAsync", onOfferAsync); chatHubConnection.on("OnOfferAsync", onOfferAsync);
chatHubConnection?.on("OnAnswerOffer", onAnswerOffer); chatHubConnection.on("OnAnswerOffer", onAnswerOffer);
chatHubConnection?.on("OnHangUpAsync", onHangUpAsync); chatHubConnection.on("OnHangUpAsync", onHangUpAsync);
chatHubConnection?.on("OnCallDeclinedAsync", onCallDeclinedAsync); chatHubConnection.on("OnCallDeclinedAsync", onCallDeclinedAsync);
} }
//Video Constraints //Video Constraints
@ -175,13 +175,13 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
args = [targetUserID, data]; args = [targetUserID, data];
//json In Data //json In Data
} }
await chatHubConnection?.invoke(invokeMethod, args: args); await chatHubConnection.invoke(invokeMethod, args: args);
} }
void stopListeners() async { void stopListeners() async {
chatHubConnection?.off('OnCallDeclinedAsync'); chatHubConnection.off('OnCallDeclinedAsync');
chatHubConnection?.off('OnCallAcceptedAsync'); chatHubConnection.off('OnCallAcceptedAsync');
chatHubConnection?.off('OnIceCandidateAsync'); chatHubConnection.off('OnIceCandidateAsync');
chatHubConnection?.off('OnAnswerOffer'); chatHubConnection.off('OnAnswerOffer');
} }
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save