Compare commits

..

6 Commits

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

@ -18,7 +18,7 @@ if (keystorePropertiesFile.exists()) {
android {
namespace 'hmg.cloudSolutions.mohem'
compileSdk 36
compileSdk 37
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).
applicationId "hmg.cloudSolutions.mohem"
minSdkVersion 30
targetSdk 35
targetSdk 37
targetSdk = flutter.targetSdkVersion
versionCode flutter.versionCode
versionName flutter.versionName
@ -60,12 +60,14 @@ android {
buildTypes {
debug {
signingConfig signingConfigs.debug
minifyEnabled false
shrinkResources false
}
release {
signingConfig signingConfigs.release
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
proguardFiles 'proguard-rules.pro'
}
}
}

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

@ -70,7 +70,16 @@ allprojects {
}
}
subprojects {
afterEvaluate { project ->
if (project.name == "huawei_push") {
project.android {
compileSdk 36
}
}
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"

@ -4,4 +4,8 @@ android.useAndroidX=true
android.enableJetifier=true
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
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

@ -30,8 +30,10 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version '8.11.0' apply false
id("org.jetbrains.kotlin.android") version "2.2.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 "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.firebase.crashlytics") version "3.0.4" apply false
id('org.gradle.toolchains.foojay-resolver-convention') version '0.9.0' apply false

@ -0,0 +1,46 @@
<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>

After

Width:  |  Height:  |  Size: 3.7 KiB

@ -0,0 +1,12 @@
<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>

After

Width:  |  Height:  |  Size: 903 B

@ -0,0 +1,3 @@
<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>

After

Width:  |  Height:  |  Size: 434 B

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

@ -1,4 +0,0 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.3339 15.9992H13.3339V11.1992C13.3339 10.9365 13.3343 10.7929 13.34 10.6886L13.341 10.673L13.3565 10.6721C13.4609 10.6664 13.6045 10.6659 13.8672 10.6659H17.3339C18.8066 10.6659 20.0005 11.8598 20.0005 13.3326C20.0005 14.8053 18.8066 15.9992 17.3339 15.9992Z" fill="#2E3039"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0684 2.33203H15.9316C13.1698 2.33202 11.005 2.33201 9.30102 2.53899C7.55973 2.75051 6.16606 3.19084 5.02088 4.16892C4.71548 4.42976 4.43104 4.71419 4.1702 5.0196C3.19212 6.16478 2.75179 7.55845 2.54027 9.29974C2.33329 11.0037 2.3333 13.1684 2.33331 15.9304V16.067C2.3333 18.829 2.33329 20.9937 2.54027 22.6977C2.75179 24.4389 3.19212 25.8326 4.1702 26.9778C4.43104 27.2832 4.71547 27.5676 5.02088 27.8285C6.16606 28.8066 7.55973 29.2469 9.30102 29.4584C11.005 29.6654 13.1697 29.6654 15.9316 29.6654H16.0683C18.8302 29.6654 20.995 29.6654 22.6989 29.4584C24.4402 29.2469 25.8339 28.8066 26.9791 27.8285C27.2845 27.5676 27.5689 27.2832 27.8298 26.9778C28.8078 25.8326 29.2482 24.4389 29.4597 22.6977C29.6667 20.9937 29.6667 18.829 29.6666 16.067V15.9304C29.6667 13.1684 29.6667 11.0037 29.4597 9.29974C29.2482 7.55845 28.8078 6.16478 27.8298 5.0196C27.5689 4.71419 27.2845 4.42976 26.9791 4.16892C25.8339 3.19084 24.4402 2.75051 22.6989 2.53899C20.995 2.33201 18.8303 2.33202 16.0684 2.33203ZM13.8672 7.99925L13.7592 7.99898C13.3835 7.99761 12.9217 7.99593 12.5098 8.12976C11.698 8.39355 11.0615 9.03003 10.7977 9.84187C10.6639 10.2538 10.6656 10.7156 10.6669 11.0913L10.6672 11.1992V23.9993C10.6672 24.7356 11.2642 25.3326 12.0005 25.3326C12.7369 25.3326 13.3339 24.7356 13.3339 23.9993V18.6659H17.3339C20.2794 18.6659 22.6672 16.2781 22.6672 13.3326C22.6672 10.3871 20.2794 7.99925 17.3339 7.99925H13.8672Z" fill="#2E3039"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

@ -1,8 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="19.5" height="19.5" viewBox="0 0 19.5 19.5">
<g id="graduation-cap" transform="translate(0)">
<path id="cap-top" d="M9.75,4.5L3.5,7.5L9.75,10.5L16,7.5L9.75,4.5Z" fill="#2e303a"/>
<path id="cap-body" d="M4.5,8.5V13.5C4.5,14.5,6.5,16.5,9.75,16.5C13,16.5,15,14.5,15,13.5V8.5L9.75,11.5L4.5,8.5Z" fill="#2e303a"/>
<path id="tassel" d="M16,8V12.5L16.5,13L17,12.5V8H16Z" fill="#2e303a"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 457 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.2 MiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.2 MiB

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32.774" height="32.788" viewBox="0 0 32.774 32.788">
<g id="park-tickets-couple" transform="translate(-0.205 0.001)">
<path id="Path_4724" data-name="Path 4724" d="M68.36,485.746a1.657,1.657,0,0,1-1.581,1.3.539.539,0,0,0-.206,1.025,1.656,1.656,0,0,1,.955,1.814l-.14.7a1.744,1.744,0,0,1-2.093,1.278c-.25-.06-.506-.106-.759-.156-.373-.073-.745-.15-1.118-.223-.493-.1-.985-.2-1.477-.3l-1.76-.353c-.655-.133-1.311-.263-1.967-.4-.7-.14-1.4-.279-2.1-.423-.719-.143-1.438-.289-2.153-.433l-2.133-.429-2.036-.409c-.622-.123-1.241-.25-1.863-.373-.539-.107-1.078-.216-1.614-.323l-1.291-.26-.888-.18c-.136-.027-.273-.057-.409-.083-.007,0-.013,0-.02,0a1.686,1.686,0,0,1-.12,1.062,1.705,1.705,0,0,1-1.87.958.5.5,0,0,0-.562.246l-.3.649a.5.5,0,0,0,.176.589c.033.027,24.018,11.084,24.058,11.094a.5.5,0,0,0,.562-.246l.3-.649a.5.5,0,0,0-.176-.589,1.7,1.7,0,0,1,1.388-3,.5.5,0,0,0,.562-.246l.3-.649a.5.5,0,0,0-.176-.589,1.7,1.7,0,0,1,1.388-3,.5.5,0,0,0,.562-.246l.3-.649a.5.5,0,0,0-.176-.589,1.7,1.7,0,0,1,1.388-3,.5.5,0,0,0,.562-.246l.329-.719a.41.41,0,0,0-.2-.546L68.416,485.5Z" transform="translate(-39.26 -469.349)" fill="#2bb8a6"/>
<path id="Path_4725" data-name="Path 4725" d="M.519,16.168c.04.02,25.932,5.214,25.975,5.214a.5.5,0,0,0,.489-.369l.14-.7a.5.5,0,0,0-.309-.532,1.7,1.7,0,0,1,.652-3.244.5.5,0,0,0,.489-.369l.1-.512.037-.19a.5.5,0,0,0-.309-.532,1.7,1.7,0,0,1,.652-3.244.5.5,0,0,0,.489-.369l.14-.7a.5.5,0,0,0-.309-.532,1.7,1.7,0,0,1,.652-3.244.5.5,0,0,0,.489-.369l.156-.775a.41.41,0,0,0-.323-.482S3.714,0,3.687,0a.412.412,0,0,0-.4.329L3.128,1.1a.5.5,0,0,0,.309.532A1.7,1.7,0,0,1,2.786,4.88.5.5,0,0,0,2.3,5.25l-.14.7a.5.5,0,0,0,.309.532,1.7,1.7,0,0,1-.652,3.244.5.5,0,0,0-.489.369l-.14.7a.5.5,0,0,0,.309.532A1.7,1.7,0,0,1,.842,14.57a.5.5,0,0,0-.489.369l-.14.7A.494.494,0,0,0,.519,16.168ZM22.7,7.639a.831.831,0,1,1,1.631.326l-1.1,5.464L22.555,16.8a.832.832,0,0,1-.815.669.9.9,0,0,1-.163-.017.833.833,0,0,1-.652-.978l.755-3.757ZM7.7,4.628a.831.831,0,0,1,1.631.326L8.948,6.84,8.519,8.98,8.173,10.7l-.616,3.081a.832.832,0,0,1-.815.669.9.9,0,0,1-.163-.017.4.4,0,0,1-.067-.017.829.829,0,0,1-.582-.962l.123-.622.323-1.611Z" fill="#125765"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

@ -1,16 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="23.959" height="37.006" viewBox="0 0 23.959 37.006">
<g id="Layer_x0020_1" transform="translate(-300.998)">
<g id="_411123120" transform="translate(301)">
<g id="Group_8657" data-name="Group 8657" transform="translate(3.252)">
<path id="Path_13674" data-name="Path 13674" d="M612.691,6.612H619.8V1.691h-7.111ZM620.669,8.3h-8.823A.833.833,0,0,1,611,7.458V.845A.847.847,0,0,1,611.846,0h8.823a.861.861,0,0,1,.845.845V7.458A.847.847,0,0,1,620.669,8.3Z" transform="translate(-607.531)" fill="#2bb8a6"/>
<path id="Path_13675" data-name="Path 13675" d="M466.845,305h15.089a.833.833,0,0,1,.845.845,5.44,5.44,0,0,1-5.42,5.441h-5.918A5.478,5.478,0,0,1,466,305.845.847.847,0,0,1,466.845,305Z" transform="translate(-465.675 -298.388)" fill="#2bb8a6"/>
<path id="Path_13676" data-name="Path 13676" d="M527.845,0A.861.861,0,0,0,527,.845a.847.847,0,0,0,.846.845h12.444a.851.851,0,0,0,.867-.845A.865.865,0,0,0,540.289,0Z" transform="translate(-525.352)" fill="#2bb8a6"/>
<g id="Group_8656" data-name="Group 8656" transform="translate(0 33.863)">
<path id="Path_13677" data-name="Path 13677" d="M455.553,1562v.867a2.276,2.276,0,1,1-4.553,0V1562h4.553Z" transform="translate(-451 -1562)" fill="#2bb8a6"/>
<path id="Path_13678" data-name="Path 13678" d="M1050.553,1562v.867a2.276,2.276,0,0,1-4.553,0V1562h4.553Z" transform="translate(-1033.101 -1562)" fill="#2bb8a6"/>
</g>
</g>
<path id="Subtraction_59" data-name="Subtraction 59" d="M20570.063,22681.4h-16.975a.817.817,0,0,1-.238-.023,3.388,3.388,0,0,1-2.236-1.018,3.476,3.476,0,0,1-1.02-2.471v-19.4a3.557,3.557,0,0,1,1.02-2.475,3.457,3.457,0,0,1,1.906-.975,1.745,1.745,0,0,0-.193.8,6.288,6.288,0,0,0,6.289,6.285h5.92a6.288,6.288,0,0,0,6.285-6.285,1.692,1.692,0,0,0-.217-.8,3.472,3.472,0,0,1,2.949,3.449v19.4a3.476,3.476,0,0,1-1.02,2.471,3.383,3.383,0,0,1-2.236,1.018A.879.879,0,0,1,20570.063,22681.4Zm-10.756-7.187a2.114,2.114,0,1,0,1.494.619A2.1,2.1,0,0,0,20559.307,22674.211Zm4.027.732-.613.621.961.939-.961.943.613.621.963-.949v0l.973.949.609-.621-.961-.943.961-.939-.609-.621-.973.951Zm-4.025-6.031a2.112,2.112,0,0,0-.807,4.063,2.071,2.071,0,0,0,.809.164,2.113,2.113,0,0,0,0-4.227Zm3.543,1.406-.645.584,1.531,1.7,2.637-2.6-.609-.619-1.992,1.963Zm-3.539-6.709a2.158,2.158,0,0,0-.486.059,2.111,2.111,0,1,0,.969,4.109,2.123,2.123,0,0,0,1.57-2.545A2.1,2.1,0,0,0,20559.313,22663.609Zm3.539,1.266-.645.58,1.531,1.7,2.637-2.6-.609-.619-1.992,1.963Z" transform="translate(-20549.596 -22648.381)" fill="#125765"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

@ -1 +0,0 @@
<?xml version="1.0" ?><svg id="Outline" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><defs><style>.cls-1{fill:#ffffff;}</style></defs><path style="fill: #FFF" class="cls-1" d="M376,170a11.34,11.34,0,0,0-16,.15L215.28,317.64l-64-65.22a11.34,11.34,0,0,0-16.18,15.88l72.16,73.58a11.35,11.35,0,0,0,16,.15,12,12,0,0,0,1.43-1.7l.4-.37,151-154A11.33,11.33,0,0,0,376,170Z"/><path style="fill: #FFF" class="cls-1" d="M256,58.65c-108.38,0-196.56,88.18-196.56,196.56S147.62,451.77,256,451.77,452.56,363.6,452.56,255.21,364.38,58.65,256,58.65Zm0,370.45c-95.88,0-173.88-78-173.88-173.89S160.12,81.33,256,81.33s173.88,78,173.88,173.88S351.88,429.1,256,429.1Z"/></svg>

Before

Width:  |  Height:  |  Size: 665 B

@ -1 +0,0 @@
<?xml version="1.0" ?><svg data-name="Livello 1" id="Livello_1" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg"><title/><path style="fill: #fff;" d="M64,0a64,64,0,1,0,64,64A64.07,64.07,0,0,0,64,0Zm0,122a58,58,0,1,1,58-58A58.07,58.07,0,0,1,64,122Z"/><path style="fill: #fff;" d="M92.12,35.79a3,3,0,0,0-4.24,0L64,59.75l-23.87-24A3,3,0,0,0,35.88,40L59.76,64,35.88,88a3,3,0,0,0,4.25,4.24L64,68.25l23.88,24A3,3,0,0,0,92.13,88L68.24,64,92.13,40A3,3,0,0,0,92.12,35.79Z"/></svg>

Before

Width:  |  Height:  |  Size: 479 B

@ -164,7 +164,6 @@
"quotationAnalysis": "تحليل الاقتباس",
"subject": "موضوعات",
"description": "وصف",
"pleaseEnterDescription": "الرجاء إدخال الوصف",
"supplier": "المورد",
"site": "موقع",
"buyer": "مشتر",
@ -259,7 +258,6 @@
"fullName": "الأسم الكامل",
"remove": "حذف",
"submit": "ارسال",
"submitAsAnonymous": "إرسال كمجهول",
"areYouSureYouWantToSubmit": "هل أنت متأكد أنك تريد أن تقدم؟",
"comments": "تعليقات",
"writeComment": "أكتب تعليقا",
@ -320,8 +318,6 @@
"createdFor": "انشاء لأجل",
"requestCreatedSuccessfully": "تم انشاء الطلب بنجاح",
"search": "بحث",
"filterEnterPrDetailsHere": "ادخل تفاصيل طلب الشراء هنا",
"filterEnterPoDetailsHere": "ادخل تفاصيل أمر الشراء هنا",
"wantToReject": "هل انت متأكد تريد الرفض",
"requestType": "نوع الطلب",
"employeeDigitalID": "هويةالموظف الرقمية",
@ -535,7 +531,7 @@
"noWinner": "حزين! لم يفز أحد اليوم.",
"myTeam": "فريقي",
"youCanPlayDemo": "لكن يمكنك لعب العرض",
"group": "مجموعة",
"group" : "مجموعة",
"searchGroup": "مجموعة البحث",
"connectHmgWifi": "قم بتوصيل HMG WIFI",
"connectedHmgWifi": "اتصال HMG WIFI",
@ -579,20 +575,20 @@
"versionStatus": "حالة الإصدار",
"supplierNo": "رقم المورد",
"general": "عام",
"requesterOperatingUnit": "وحدة تشغيل مقدم الطلب",
"prepareEmpNum": "إعداد رقم الموظف",
"supplierInfo": "معلومات المورد",
"requesterOperatingUnit":"وحدة تشغيل مقدم الطلب",
"prepareEmpNum":"إعداد رقم الموظف",
"supplierInfo" : "معلومات المورد",
"supplierAcNo": "رقم حساب المورد",
"supplierAcName": "اسم حساب المورد",
"supplierIBAN": "رقم IBAN للمورد",
"supplierCRNo": "رقم السجل التجاري",
"suppliedAcNo": "رقم الحساب المقدم",
"patientRefundInvoice": "فاتورة استرداد الأموال للمريض",
"patientNumber": "رقم المريض",
"patientName": "اسم المريض",
"invoiceDate": "تاريخ الفاتورة",
"refundInvoice": "فاتورة الاسترجاع",
"hospitalClinic": "عيادة المستشفى",
"supplierIBAN" : "رقم IBAN للمورد",
"supplierCRNo" :"رقم السجل التجاري",
"suppliedAcNo" : "رقم الحساب المقدم",
"patientRefundInvoice" : "فاتورة استرداد الأموال للمريض",
"patientNumber" : "رقم المريض",
"patientName" : "اسم المريض",
"invoiceDate" : "تاريخ الفاتورة",
"refundInvoice" :"فاتورة الاسترجاع",
"hospitalClinic" : "عيادة المستشفى",
"graphicalAnalysis": "التحليل الرسومي",
"itemHistoryAnalysis": "تحليل تاريخ العنصر",
"pOno": "امر شراء #",
@ -618,23 +614,23 @@
"members": "الأعضاء",
"searchByUserName": "البحث بواسطة اسم المستخدم",
"shareScreen": "مشاركة الشاشة",
"start": "يبدأ",
"about": "عن",
"start":"يبدأ",
"about":"عن",
"explore": "يستكشف",
"mazaya": "مازيا",
"benefits": "فوائد",
"mazayaDesc": "اكتشف الخصومات والعروض الخاصة المتاحة للموظفين",
"viewallofferMazaya": "أعرض كل المزايا",
"viewallofferMazaya" : "أعرض كل المزايا",
"buyerName": "اسم المشتري",
"buyerNumber": "رقم المشتري",
"highestBidder": "أعلى مزايد",
"remarks": "ملاحظات",
"faHeader": "تفاصيل رأس الصفحة FA",
"bookTypeCode": "رمز نوع الكتاب",
"bookTypeCode": "رمز نوع الكتاب",
"categoryCode": "رمز الفئة",
"categoryGroup": "مجموعة الفئات",
"faLINES": "تفاصيل الأصول",
"assetNumber": "رقم الأصول",
"faLINES": "تفاصيل الأصول",
"assetNumber": "رقم الأصول",
"assetDescription": "وصف الأصول",
"barCodeNumber": "رقم الباركود",
"datePlaceInService": "تاريخ وضعه في الخدمة",
@ -642,43 +638,18 @@
"disposedDate": "تاريخ التخلص",
"netBookValue": "القيمة الدفترية الصافية",
"purchasedPrice": "سعر الشراء",
"usefulLife": "العمر الإنتاجي",
"usefulLife": "العمر الإنتاجي",
"yearsUsed": "سنوات الاستخدام",
"faRequest": "طلب التخلص من FA",
"showMore": "عرض المزيد",
"ticketDetails": "تفاصيل التذكرة",
"currentTicketBalance": "رصيد التذكرة الحالي",
"ticketHistory": "تاريخ التذكرة",
"proceedBooking": "المتابعة للحجز",
"courses": "الدورات التدريبية",
"course": "دورة",
"status": "الحالة",
"deadline": "الموعد النهائي",
"overdue": "متأخر",
"noCoursesAvailable": "لا توجد دورات متاحة",
"pullToRefresh": "اسحب للتحديث",
"courseUrlNotAvailable": "رابط الدورة غير متوفر",
"buyerDetails": "تفاصيل المشتري",
"etqanOVR": "الإبلاغ عن حادثة",
"etqanRequest": "طلبات الحوادث",
"selectProject": "حدد المشروع",
"project": "مشروع",
"keen": "حريص",
"reportIncident": "الإبلاغ عن الحادث",
"myIncidents": "حوادثي",
"incidentDetails": "تفاصيل الحادث",
"ticketNumber": "رقم التذكرة",
"ticketId": "معرف التذكرة",
"pleaseSelectProject": "الرجاء اختيار مشروع",
"viewAttachment" : "عرض المرفق",
"parkingQr": "وقوف السيارات QR",
"startNow": "ابدأ الآن",
"destination_type": "نوع الوجهة",
"source_organization_name": "اسم منظمة المصدر",
"destination_organization_name": "اسم منظمة الوجهة",
"costCenter": "مركز التكلفة",
"priceSAR": "السعر (ريال سعودي)",
"amountSAR": "المبلغ (ريال سعودي)",
"amuLastThreeMonths": "AMU (آخر 3 أشهر)"
}
"chatBot": "المساعد الذكي",
"helloAskMe": "مرحباً، اسألني",
"anything": "عن أي شيء...",
"thinking": "جاري التفكير",
"waitingForResponse": "في انتظار الرد...",
"typeAMessage": "اكتب رسالة...",
"initializingChatPleaseWait": "يرجى الانتظار، جاري تهيئة المحادثة...",
"am": "ص",
"pm": "م"
}

@ -158,7 +158,6 @@
"subject": "Subject",
"quotationAnalysis": "Quotation Analysis",
"description": "Description",
"pleaseEnterDescription": "Please enter description",
"supplier": "Supplier",
"site": "Site",
"buyer": "Buyer",
@ -257,7 +256,6 @@
"remove": "Remove",
"Attendance": "Attendance",
"submit": "Submit",
"submitAsAnonymous": "Submit as Anonymous",
"areYouSureYouWantToSubmit": "Are you sure you want to submit?",
"comments": "Comments",
"writeComment": "Write a comment",
@ -315,8 +313,6 @@
"requestType": "Request Type",
"requestCreatedSuccessfully": "Request created successfully",
"search": "Search",
"filterEnterPrDetailsHere": "Enter PR Details here",
"filterEnterPoDetailsHere": "Enter PO Details here",
"wantToReject": "Are you sure want to reject?",
"employeeDigitalID": "Employee Digital ID",
"businessCard": "Business Card",
@ -569,7 +565,7 @@
"addAtLeastOneAttachment": "Please add at least one attachment.",
"pleaseClickButtonToJoinMarathon": "Press the button below to join the Marathon.",
"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",
"paymentDetails": "Payment Details",
@ -595,19 +591,19 @@
"versionStatus": "Version Status",
"supplierNo": "Supplier No",
"general": "General",
"prepareEmpNum": "Prepare Employee Num",
"supplierInfo": "Supplier Information",
"prepareEmpNum":"Prepare Employee Num",
"supplierInfo" : "Supplier Information",
"supplierAcNo": "Supplier Account No",
"supplierAcName": "Supplier Account Name",
"supplierIBAN": "Supplier IBAN",
"supplierCRNo": "CR Number",
"suppliedAcNo": "Supplied Account No.",
"patientRefundInvoice": "Patient Refund Invoice",
"patientNumber": "Patient Number",
"patientName": "Patient Name",
"invoiceDate": "Invoice Date",
"refundInvoice": "Refund Invoice",
"hospitalClinic": "Hospital Clinic",
"supplierAcName":"Supplier Account Name",
"supplierIBAN" : "Supplier IBAN",
"supplierCRNo" : "CR Number",
"suppliedAcNo" : "Supplied Account No.",
"patientRefundInvoice" : "Patient Refund Invoice",
"patientNumber" : "Patient Number",
"patientName" : "Patient Name",
"invoiceDate" : "Invoice Date",
"refundInvoice" :"Refund Invoice",
"hospitalClinic" : "Hospital Clinic",
"graphicalAnalysis": "Graphical Analysis",
"itemHistoryAnalysis": "Item History Analysis",
"pOno": "P.O #",
@ -616,23 +612,23 @@
"qtyReceived": "Qty. Received",
"bonusQty": "Bonus Qty.",
"balQty": "Bal. Qty.",
"start": "Start",
"about": "About",
"start":"Start",
"about":"About",
"explore": "Explore",
"mazaya": "MAZAYA",
"benefits": "Benefits",
"mazayaDesc": "Discover special Discounts and offers available to Employees",
"viewallofferMazaya": "View All Offers",
"viewallofferMazaya" : "View All Offers",
"buyerName": "Buyer Name",
"buyerNumber": "Buyer Number",
"highestBidder": "Highest Bidder",
"remarks": "Remarks",
"faHeader": "FA Header Details",
"bookTypeCode": "Book Type Code",
"bookTypeCode": "Book Type Code",
"categoryCode": "Category Code",
"categoryGroup": "Category Group",
"faLINES": "Asset Details",
"assetNumber": "Asset Number",
"faLINES": "Asset Details",
"assetNumber": "Asset Number",
"assetDescription": "Asset Description",
"barCodeNumber": "BarCode Number",
"datePlaceInService": "Date place In Service",
@ -640,42 +636,18 @@
"disposedDate": "Disposed Date",
"netBookValue": "Net Book Value",
"purchasedPrice": "Purchased Price",
"usefulLife": "Useful Life",
"usefulLife": "Useful Life",
"yearsUsed": "Years Used",
"faRequest": "FA Disposal Request",
"faRequest":"FA Disposal Request",
"showMore": "Show More",
"ticketDetails": "Ticket Details",
"currentTicketBalance": "Current Ticket Balance",
"ticketHistory": "Ticket History",
"proceedBooking": "Proceed For Booking",
"courses": "Courses",
"course": "Course",
"status": "Status",
"deadline": "Deadline",
"overdue": "Overdue",
"noCoursesAvailable": "No Courses Available",
"pullToRefresh": "Pull to refresh",
"courseUrlNotAvailable": "Course URL not available",
"buyerDetails": "Buyer Details",
"etqanOVR": "Report An Incident",
"etqanRequest": "Incident Requests",
"selectProject": "Select Project",
"project": "Project",
"keen": "Keen",
"reportIncident": "Report Incident",
"myIncidents": "My Incidents",
"incidentDetails": "Incident Details",
"ticketNumber": "Ticket Number",
"ticketId": "Ticket ID",
"pleaseSelectProject": "Please select a project",
"viewAttachment" : "View Attachment",
"parkingQr": "Parking QR",
"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)"
"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"
}

@ -0,0 +1,699 @@
#!/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

@ -20,7 +20,5 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
</dict>
</plist>

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 60;
objects = {
/* Begin PBXBuildFile section */
@ -12,6 +12,7 @@
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
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 */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
@ -39,6 +40,7 @@
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>"; };
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>"; };
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>"; };
@ -57,6 +59,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
1CCF895CFB3C95DEBB84A05F /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -75,6 +78,7 @@
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
@ -144,13 +148,16 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
C4CFBC4C5CAC00182015ACD5 /* [CP] Embed Pods Frameworks */,
EF29CE25BB57F5AA48CE1136 /* [CP] Copy Pods Resources */,
D9FFC0F21D4146458615A992 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
packageProductDependencies = (
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
);
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
@ -179,6 +186,9 @@
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
packageReferences = (
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
);
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
@ -242,23 +252,6 @@
shellPath = /bin/sh;
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 */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@ -291,6 +284,23 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
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 */
/* Begin PBXSourcesBuildPhase section */
@ -367,7 +377,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -454,7 +464,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -504,7 +514,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -600,6 +610,20 @@
defaultConfigurationName = Release;
};
/* 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 */;
}

@ -5,6 +5,24 @@
<BuildAction
parallelizeBuildables = "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>
<BuildActionEntry
buildForTesting = "YES"

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

@ -2,6 +2,27 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<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>
<true/>
<key>CFBundleDevelopmentRegion</key>
@ -56,13 +77,13 @@
<key>NSLocationWhenInUseUsageDescription</key>
<string>This App requires access to your location to mark your attendance.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app requires microphone access to for call.</string>
<string>This app requires microphone access for calls and speech-to-text input.</string>
<key>NSPhotoLibraryUsageDescription</key>
<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>
<true/>
<key>NSSpeechRecognitionUsageDescription</key>
<string>This app requires speech recognition access to access voice command features.</string>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>

@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:http/io_client.dart';
@ -19,11 +18,15 @@ class APIError {
dynamic errorCode;
int? errorType;
String? errorMessage;
int? errorStatusCode;
int? 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
String toString() {
@ -45,22 +48,22 @@ APIException _throwAPIException(Response response) {
APIError? apiError;
if (response.body != null && response.body.isNotEmpty) {
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']);
}
return APIException(APIException.BAD_REQUEST, error: apiError);
case 401:
return const APIException(APIException.UNAUTHORIZED);
return APIException(APIException.UNAUTHORIZED);
case 403:
return const APIException(APIException.FORBIDDEN);
return APIException(APIException.FORBIDDEN);
case 404:
return const APIException(APIException.NOT_FOUND);
return APIException(APIException.NOT_FOUND);
case 500:
return const APIException(APIException.INTERNAL_SERVER_ERROR);
return APIException(APIException.INTERNAL_SERVER_ERROR);
case 444:
var downloadUrl = response.headers["location"];
return APIException(APIException.UPGRADE_REQUIRED, arguments: downloadUrl);
default:
return const APIException(APIException.OTHER);
return APIException(APIException.OTHER);
}
}
@ -71,16 +74,8 @@ class ApiClient {
factory ApiClient() => _instance;
Future<U> postJsonForObject<T, U>(
FactoryConstructor<U> factoryConstructor,
String url,
T jsonObject, {
String? token,
Map<String, dynamic>? queryParameters,
Map<String, String>? headers,
int retryTimes = 0,
bool isFormData = false,
}) async {
Future<U> postJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, 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'};
if (headers != null && headers.isNotEmpty) {
_headers.addAll(headers);
@ -126,15 +121,8 @@ class ApiClient {
}
}
Future<Response> postJsonForResponse<T>(
String url,
T jsonObject, {
String? token,
Map<String, dynamic>? queryParameters,
Map<String, String>? headers,
int retryTimes = 0,
bool isFormData = false,
}) async {
Future<Response> postJsonForResponse<T>(String url, T jsonObject,
{String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0, bool isFormData = false}) async {
String? requestBody;
late Map<String, String> stringObj;
if (jsonObject != null) {
@ -169,9 +157,9 @@ class ApiClient {
var queryString = new Uri(queryParameters: queryParameters).query;
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;
} else {
throw _throwAPIException(response);
@ -179,7 +167,7 @@ class ApiClient {
} on SocketException catch (e) {
if (retryTimes > 0) {
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);
} else {
throw APIException(APIException.OTHER, arguments: e);
@ -187,7 +175,7 @@ class ApiClient {
} on HttpException catch (e) {
if (retryTimes > 0) {
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);
} else {
throw APIException(APIException.OTHER, arguments: e);
@ -197,7 +185,7 @@ class ApiClient {
} on ClientException catch (e) {
if (retryTimes > 0) {
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);
} else {
throw APIException(APIException.OTHER, arguments: e);
@ -236,7 +224,7 @@ class ApiClient {
var queryString = new Uri(queryParameters: queryParameters).query;
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) {
return response;
@ -246,7 +234,7 @@ class ApiClient {
} on SocketException catch (e) {
if (retryTimes > 0) {
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);
} else {
throw APIException(APIException.OTHER, arguments: e);
@ -254,7 +242,7 @@ class ApiClient {
} on HttpException catch (e) {
if (retryTimes > 0) {
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);
} else {
throw APIException(APIException.OTHER, arguments: e);
@ -264,7 +252,7 @@ class ApiClient {
} on ClientException catch (e) {
if (retryTimes > 0) {
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);
} else {
throw APIException(APIException.OTHER, arguments: e);

@ -1,12 +1,10 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:http/http.dart' as http;
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/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart';
@ -16,15 +14,10 @@ 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/itg_forms_model.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/generic_response_model.dart';
import 'package:mohem_flutter_app/models/get_employee_parking_details_model.dart';
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_response_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';
@ -37,7 +30,7 @@ class DashboardApiClient {
Future<GetAttendanceTracking?> getAttendanceTracking() async {
String url = "${ApiConsts.erpRest}GET_Attendance_Tracking";
Map<String, dynamic> postParams = <String, dynamic>{};
Map<String, dynamic> postParams = {};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
@ -51,7 +44,7 @@ class DashboardApiClient {
Future<GenericResponseModel?> getOpenNotifications() async {
String url = "${ApiConsts.erpRest}GET_OPEN_NOTIFICATIONS";
Map<String, dynamic> postParams = <String, dynamic>{};
Map<String, dynamic> postParams = {};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
@ -65,7 +58,7 @@ class DashboardApiClient {
Future<GenericResponseModel?> getCOCNotifications() async {
String url = "${ApiConsts.cocRest}Mohemm_ITG_ReviewerAdmin_Pending_Tasks";
Map<String, dynamic> postParams = <String, dynamic>{"Date": DateUtil.getISODateFormat(DateTime.now()), "EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER};
Map<String, dynamic> postParams = {"Date": DateUtil.getISODateFormat(DateTime.now()), "EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
@ -79,7 +72,7 @@ class DashboardApiClient {
Future<ItgFormsModel?> getItgFormsPendingTask() async {
String url = "${ApiConsts.cocRest}ITGFormsPendingTasks";
Map<String, dynamic> postParams = <String, dynamic>{};
Map<String, dynamic> postParams = {};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
@ -93,13 +86,13 @@ class DashboardApiClient {
Future<List<GetAccrualBalancesList>> getAccrualBalances(String effectiveDate, {String? empID}) async {
String url = "${ApiConsts.erpRest}GET_ACCRUAL_BALANCES";
Map<String, dynamic> postParams = <String, dynamic>{"P_EFFECTIVE_DATE": effectiveDate};
Map<String, dynamic> postParams = {"P_EFFECTIVE_DATE": effectiveDate};
postParams.addAll(AppState().postParamsJson);
if (empID != null) postParams["P_SELECTED_EMPLOYEE_NUMBER"] = empID;
return await ApiClient().postJsonForObject(
(json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json);
return responseData.getAccrualBalancesList ?? <GetAccrualBalancesList>[];
return responseData.getAccrualBalancesList ?? [];
},
url,
postParams,
@ -108,13 +101,13 @@ class DashboardApiClient {
Future<List<GetTicketAccuralBalanceModel>> getTicketAccuralBalance(String effectiveDate, {String? empID}) async {
String url = "${ApiConsts.erpRest}GET_TICKET_ACCRUAL_BALANCES";
Map<String, dynamic> postParams = <String, dynamic>{"P_EFFECTIVE_DATE": effectiveDate};
Map<String, dynamic> postParams = {"P_EFFECTIVE_DATE": effectiveDate};
postParams.addAll(AppState().postParamsJson);
if (empID != null) postParams["P_SELECTED_EMPLOYEE_NUMBER"] = empID;
return await ApiClient().postJsonForObject(
(json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json);
return responseData.getAccrualBalancesTicketList ?? <GetTicketAccuralBalanceModel>[];
return responseData.getAccrualBalancesTicketList ?? [];
},
url,
postParams,
@ -123,7 +116,7 @@ class DashboardApiClient {
Future<GenericResponseModel?> getOpenMissingSwipes() async {
String url = "${ApiConsts.erpRest}GET_OPEN_MISSING_SWIPES";
Map<String, dynamic> postParams = <String, dynamic>{};
Map<String, dynamic> postParams = {};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
@ -138,12 +131,12 @@ class DashboardApiClient {
//Menus List
Future<List<ListMenu>> getListMenu() async {
String url = "${ApiConsts.erpRest}GET_MENU";
Map<String, dynamic> postParams = <String, dynamic>{};
Map<String, dynamic> postParams = {};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json);
return responseData.listMenu ?? <ListMenu>[];
return responseData.listMenu ?? [];
},
url,
postParams,
@ -153,7 +146,7 @@ class DashboardApiClient {
//GET_MENU_ENTRIES
Future<GenericResponseModel?> getGetMenuEntries() async {
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);
return await ApiClient().postJsonForObject(
(json) {
@ -167,7 +160,7 @@ class DashboardApiClient {
Future<GenericResponseModel?> getEventActivity() async {
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);
return await ApiClient().postJsonForObject(
(json) {
@ -181,7 +174,7 @@ class DashboardApiClient {
Future<GenericResponseModel?> getTicketBookingRedirection() async {
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);
return await ApiClient().postJsonForObject(
(json) {
@ -195,7 +188,7 @@ class DashboardApiClient {
Future<GenericResponseModel?> getTicketBalance() async {
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);
return await ApiClient().postJsonForObject(
@ -212,7 +205,7 @@ class DashboardApiClient {
String url = "${ApiConsts.ssoAuthRedirection}?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 = <String, dynamic>{
Map<String, dynamic> postParams = {
"ClientId": clientID,
// "ClientId": "a9f4d1a0596d4aea8f830992ec4bdac1",
"PersonId": AppState().memberInformationList?.eMPLOYEENUMBER,
@ -231,35 +224,19 @@ class DashboardApiClient {
);
}
Future<GetEitAttachmentTicket?> getTicketEitPDFDownload({required String peiExtraInfoId}) 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}) async {
token =
"eyJhbGciOiJSUzI1NiIsImtpZCI6IjhjZTE2OWM0YjIwYjQ2ZWM5YTQyOTU3Y2ZhODUzNzQ1IiwidHlwIjoiSldUIn0.eyJ0ZW5hbnRfaWQiOiJhOWY0ZDFhMDU5NmQ0YWVhOGY4MzA5OTJlYzRiZGFjMSIsImVpZCI6IjExNzkzMCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3NpZCI6Ijk2MDI0OGM1NzA3YzQ3MmFhYTEzM2I1N2ZhODE1ZmVhIiwibGFuZ3VhZ2UiOiJVUyIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6IjExNzkzMEBobWcuY29tIiwiZXhwIjoxNzgyNDc1NzY5LCJpc3MiOiJodHRwczovL3Nzby11YXQuaG1nLmNvbSIsImF1ZCI6ImE5ZjRkMWEwNTk2ZDRhZWE4ZjgzMDk5MmVjNGJkYWMxIn0.rJcLVsG8D0XECyLERCTD2uqGeWyvp-OBVGE9uL2qKrX4etFUHgdFt_5kYF6edFTtGy-0PIZadHDmv7e-IOhVWHm5HVMClaukiXoRXR8cDN8XA1wfme3Kd-U5PXN-IRh49AyRTzLO0rYNPvH81ScosWGlsFSkOvA-0hJNa2adHdtvgNvB8wJshSU5p7sAmF8mjdDY6aInG19etu2iEuUDwHHA4ZY_ts4hboHo8fE392hFaYGonExoD7bpW5RMx5xKWeRCmWpG_PK8Aw_z1jGzdB9PANus4pteRGuln1J-kmo2lQC9pVrSyZATAKp1HfgfyZ_vUhaHEfM69cMWaCslJQ";
// var 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'));
request.fields.addAll({'JWTToken': token});
// request.headers.addAll(headers);
Future<dynamic> getBookingSSOFinalRedirection({required String token, required String refreshToken}) async {
// token =
// "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'));
// http.MultipartRequest request = http.MultipartRequest('POST', Uri.parse('https://ek.techmaster.in/SSO/HMG'));
request.fields.addAll(<String, String>{'JWTToken': token, 'RefreshToken': refreshToken});
http.StreamedResponse response = await request.send();
if (response.statusCode == 302) {
String res = await response.stream.bytesToString();
print("================== post ==========");
var res = await response.stream.bytesToString();
return response.headers["location"];
} else {
print(response.reasonPhrase);
@ -276,22 +253,22 @@ class DashboardApiClient {
String QRValue = "",
String payrollCode = "",
}) async {
var _mobileDeviceIdentifierPlugin = MobileDeviceIdentifier();
Codec<String, String> stringToBase64 = utf8.fuse(base64);
String url = "${ApiConsts.swpRest}AuthenticateAndSwipeUserSupportNFC";
Uuid uuid = Uuid();
var uuid = Uuid();
String? deviceId = "";
// Generate a v4 (random) id
if (Platform.isAndroid) {
AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo;
deviceId = await _mobileDeviceIdentifierPlugin.getDeviceId() ?? androidInfo.id;
deviceId = await androidInfo.id;
deviceId = stringToBase64.encode(deviceId);
deviceId = deviceId.length >= 50 ? deviceId.substring(0, 48) : deviceId;
} else if (Platform.isIOS) {
IosDeviceInfo iosInfo = await DeviceInfoPlugin().iosInfo;
deviceId = iosInfo.identifierForVendor;
}
Map<String, dynamic> postParams = <String, dynamic>{
Map<String, dynamic> postParams = {
"UID": deviceId, //uuid.v4(), //Mobile Id
// "UID": uuid.v4(), //Mobile Id
"Latitude": lat,
@ -317,10 +294,10 @@ class DashboardApiClient {
//Mark Fake Location
Future<GenericResponseModel?> markFakeLocation({String lat = "0", String? long = "0", required String sourceName}) async {
String url = "${ApiConsts.swpRest}CreateIssueInfo";
Uuid uuid = Uuid();
var uuid = Uuid();
// Generate a v4 (random) id
Map<String, dynamic> postParams = <String, dynamic>{
Map<String, dynamic> postParams = {
"UID": uuid.v4(), //Mobile Id
"Latitude": lat,
"Longitude": long,
@ -344,7 +321,7 @@ class DashboardApiClient {
Future<MohemmItgResponseItem?> getITGPageNotification() async {
String url = "${ApiConsts.cocRest}Mohemm_ITG_GetPageNotification";
Map<String, dynamic> postParams = <String, dynamic>{
Map<String, dynamic> postParams = {
"EmployeeNumber": AppState().getUserName,
"ItgEnableAt": "After Service Submission", //Mobile Id
"ItgServiceName": "Login",
@ -362,26 +339,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
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";
Map<String, dynamic> postParams = <String, dynamic>{
Map<String, dynamic> postParams = {
"EmployeeNumber": AppState().getUserName,
"ItgComments": comment,
"ItgNotificationMasterId": masterId,
@ -402,7 +364,7 @@ class DashboardApiClient {
Future<ItgMainRes?> getAdvertisementDetail(String masterID) async {
String url = "${ApiConsts.cocRest}Mohemm_ITG_GetPageNotificationDetails";
Map<String, dynamic> postParams = <String, dynamic>{
Map<String, dynamic> postParams = {
"EmployeeNumber": AppState().getUserName,
"ItgNotificationMasterId": masterID, //Mobile Id
};
@ -420,7 +382,7 @@ class DashboardApiClient {
Future setAdvertisementViewed(String masterID, int advertisementId, String? ackValue) async {
String url = "${ApiConsts.cocRest}Mohemm_ITG_UpdateAdvertisementAsViewed";
Map<String, dynamic> postParams = <String, dynamic>{
Map<String, dynamic> postParams = {
"ItgNotificationMasterId": masterID,
"EmployeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER.toString(),
"ItgAdvertisementId": advertisementId,
@ -452,50 +414,27 @@ class DashboardApiClient {
// );
// }
Future<GetEmployeeParking?> getEmployeeParkingQrCode() async {
String url = "${ApiConsts.erpRest}GET_Employee_Parking";
Future<GenericResponseModel?> getChatBotSession() async {
String url = "${ApiConsts.erpRest}Atlas_InitiateChat";
Map<String, dynamic> postParams = {};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
GetEmployeeParking responseData = GetEmployeeParking.fromJson(json);
return responseData;
},
url,
postParams,
);
}
//Fetch All Courses Status
Future<CoursesResponseModel?> fetchAllCoursesStatus() async {
String url = ApiConsts.eLearningBaseUrl + "Fetch_All_Courses_Status.php";
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',
'Content-Type': 'application/x-www-form-urlencoded',
};
Map<String, dynamic> postParams = {"username": AppState().memberInformationList?.eMPLOYEENUMBER ?? ""};
return await ApiClient().postJsonForObject(
(json) {
CoursesResponseModel responseData = CoursesResponseModel.fromJson(json);
GenericResponseModel responseData = GenericResponseModel.fromJson(json);
return responseData;
},
url,
postParams,
headers: headers,
isFormData: false,
);
}
Future<GetGreetingCardsModelResponse?> getGreetingCards() async {
String url = "${ApiConsts.erpRest}Get_GreetingCards";
Map<String, dynamic> 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) {
GetGreetingCardsModelResponse responseData = GetGreetingCardsModelResponse.fromJson(json);
GenericResponseModel responseData = GenericResponseModel.fromJson(json);
return responseData;
},
url,

@ -25,23 +25,16 @@ class EITApiClient {
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(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) {
List<List<CollectionTransaction>> responseData = [];
json['GetEITTransactionList'].forEach((element) {
var transactionList = GetEitTransactionsModel.fromJson(element).collectionTransaction;
if (transactionList != null && transactionList.isNotEmpty) {
responseData.add(transactionList);
}
if (transactionList != null) responseData.add(transactionList);
});
return responseData;
} else {
List<CollectionTransaction>? responseData = GetEitTransactionsModel.fromJson(json['GetEITTransactionList'][0]).collectionTransaction;
return responseData ?? <CollectionTransaction>[];
return responseData;
}
},
url,

@ -1,140 +0,0 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:intl/intl.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/classes/consts.dart';
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_employee_incident_report.dart';
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_requests_model.dart';
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_getprojects_model.dart';
import 'package:mohem_flutter_app/models/generic_response_model.dart';
import 'package:mohem_flutter_app/models/mowadhafhi/get_department_sections.dart';
import 'package:mohem_flutter_app/models/mowadhafhi/get_project_departments.dart';
import 'package:mohem_flutter_app/models/mowadhafhi/get_projects.dart';
import 'package:mohem_flutter_app/models/mowadhafhi/get_section_topics.dart';
import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_details.dart';
import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_transactions.dart';
import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_types.dart';
import 'package:mohem_flutter_app/models/mowadhafhi/get_tickets_list.dart';
import 'package:mohem_flutter_app/models/mowadhafhi/get_transaction_attachment_model.dart';
class EtqanApiClient {
static final EtqanApiClient _instance = EtqanApiClient._internal();
EtqanApiClient._internal();
factory EtqanApiClient() => _instance;
Future<List<EtqanGetProjectsResponse>> getEtqanProjects() async {
String url = "${ApiConsts.cocRest}ETQAN_GetProjects";
Map<String, dynamic> postParams = <String, dynamic>{"P_SELECTED_EMPLOYEE_NUMBER": AppState().memberInformationList?.eMPLOYEENUMBER};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
List<EtqanGetProjectsResponse>? responseData = <EtqanGetProjectsResponse>[];
if (json["ETQAN_GetProjects_Response"] != null && json["ETQAN_GetProjects_Response"].isNotEmpty) {
json["ETQAN_GetProjects_Response"].forEach((element) {
responseData.add(EtqanGetProjectsResponse.fromJson(element));
});
}
return responseData;
},
url,
postParams,
);
}
Future<List<EtqanGetEmployeeOvrRequestsResponse>> getEmployeeEtqanRequests() async {
String url = "${ApiConsts.cocRest}ETQAN_GetEmployeeOVRRequests";
Map<String, dynamic> postParams = <String, dynamic>{
"ETQAN_EmployeeID": AppState().memberInformationList?.eMPLOYEENUMBER,
"P_SELECTED_EMPLOYEE_NUMBER": AppState().memberInformationList?.eMPLOYEENUMBER,
};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
List<EtqanGetEmployeeOvrRequestsResponse> responseData = <EtqanGetEmployeeOvrRequestsResponse>[];
if (json["ETQAN_GetEmployeeOVRRequests_Response"] != null && json["ETQAN_GetEmployeeOVRRequests_Response"].isNotEmpty) {
json["ETQAN_GetEmployeeOVRRequests_Response"].forEach((element) {
responseData.add(EtqanGetEmployeeOvrRequestsResponse.fromJson(element));
});
}
if (responseData.isNotEmpty) {
responseData.sort((EtqanGetEmployeeOvrRequestsResponse a, EtqanGetEmployeeOvrRequestsResponse b) {
if (a.createdDate == null && b.createdDate == null) return 0;
if (a.createdDate == null) return 1;
if (b.createdDate == null) return -1;
try {
// Parse dates in format "26-Feb-2026 12:54 PM" or "01-Mar-2026 02:45 PM"
DateFormat dateFormat = DateFormat("dd-MMM-yyyy hh:mm a", "en_US");
DateTime dateA = dateFormat.parse(a.createdDate!);
DateTime dateB = dateFormat.parse(b.createdDate!);
return dateB.compareTo(dateA); // Descending order (newest first)
} catch (e) {
return (b.createdDate ?? '').compareTo(a.createdDate ?? '');
}
});
}
return responseData;
},
url,
postParams,
);
}
Future<List<EtqanGetIncidentRequestResponse>> getEtqanIncidentRequests(String ticketNo) async {
String url = "${ApiConsts.cocRest}ETQAN_GetIncidentRequest";
Map<String, dynamic> postParams = <String, dynamic>{"ETQAN_TicketNumber": ticketNo, "P_SELECTED_EMPLOYEE_NUMBER": AppState().memberInformationList?.eMPLOYEENUMBER};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
List<EtqanGetIncidentRequestResponse> responseData = <EtqanGetIncidentRequestResponse>[];
if (json["ETQAN_GetIncidentRequest_Response"] != null && json["ETQAN_GetIncidentRequest_Response"].isNotEmpty) {
json["ETQAN_GetIncidentRequest_Response"].forEach((element) {
responseData.add(EtqanGetIncidentRequestResponse.fromJson(element));
});
}
return responseData;
},
url,
postParams,
);
}
Future<Map<String, dynamic>?> createRequest(String description, int? projectID, String? attachmentBase64, bool? isAnonymous) async {
String url = "${ApiConsts.cocRest}ETQAN_CreateIncident";
Map<String, dynamic> postParams = <String, dynamic>{
"P_SELECTED_EMPLOYEE_NUMBER": AppState().memberInformationList?.eMPLOYEENUMBER,
"P_USER_NAME": AppState().memberInformationList?.eMPLOYEENUMBER,
"ETQAN_Emp_EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER,
"ETQAN_Description": description,
"ETQAN_ProjectId": projectID,
"ETQAN_FileInfoBase64": attachmentBase64,
"ETQAN_IsAnonymous": isAnonymous ?? false,
};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject(
(json) {
if (json["ETQAN_CreateIncident_Response"] != null) {
var response = json["ETQAN_CreateIncident_Response"];
if (response["id"] != null && response["ticketNumber"] != null) {
return {"id": response["id"], "ticketNumber": response["ticketNumber"]};
}
return null;
}
return null;
// "ETQAN_CreateIncident_Response": {
// "id": "00adabe4-4a32-4b35-1f00-08de4d152e7c",
// "ticketNumber": "OVR/OLY/2026/224"
// },
},
url,
postParams,
);
}
}

@ -71,14 +71,7 @@ class LoginApiClient {
Future<BasicMemberInformationModel?> mohemmSendActivationCodeByOTPNotificationType(int isMobileFingerPrint, String? mobileNumber, int optSendType, String? pUserName) async {
String url = "${ApiConsts.erpRest}Mohemm_SendActivationCodebyOTPNotificationType";
Map<String, dynamic> postParams = {
"IsMobileFingerPrint": isMobileFingerPrint,
"MobileNumber": mobileNumber,
"OTP_SendType": optSendType,
"P_USER_NAME": pUserName,
"NotificationCenterAppGUID": ApiConsts.notificationCenterAppGUID,
"NotificationCenterAppSeviceGUID": ApiConsts.notificationCenterLoginServiceGUID,
};
Map<String, dynamic> postParams = {"IsMobileFingerPrint": isMobileFingerPrint, "MobileNumber": mobileNumber, "OTP_SendType": optSendType, "P_USER_NAME": pUserName};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject((json) => GenericResponseModel.fromJson(json).basicMemberInformation, url, postParams);
}
@ -112,13 +105,7 @@ class LoginApiClient {
Future<GenericResponseModel?> sendPublicActivationCode(String? mobileNumber, String? pUsername) async {
String url = "${ApiConsts.erpRest}SendPublicActivationCode";
Map<String, dynamic> postParams = {
"MobileNumber": mobileNumber,
"P_MOBILE_NUMBER": mobileNumber,
"P_USER_NAME": pUsername,
"NotificationCenterAppGUID": ApiConsts.notificationCenterAppGUID,
"NotificationCenterAppSeviceGUID": ApiConsts.notificationCenterForgotPasswordServiceGUID,
};
Map<String, dynamic> postParams = {"MobileNumber": mobileNumber, "P_MOBILE_NUMBER": mobileNumber, "P_USER_NAME": pUsername};
postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject((json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json);

@ -90,7 +90,7 @@ class AppState {
String get getHuaweiPushToken => _huaweiPushToken;
final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 10.1, mobileType: Platform.isAndroid ? "android" : "ios");
final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 9.5, mobileType: Platform.isAndroid ? "android" : "ios");
void setPostParamsInitConfig() {
isAuthenticated = false;

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

@ -5,20 +5,13 @@ class ApiConsts {
// static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA 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://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.20.200.111:1010/";
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/api/"; // Elearning server live
static String enSATLiveURL = "https://ensat.hmg.com/auth/autologin"; // ENSAT server live
static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver
// static String baseUrl = "http://10.20.200.111:1010/";
// static String baseUrl = "https://webservices.hmg.com"; // PreProd
@ -34,8 +27,7 @@ class ApiConsts {
static String user = baseUrlServices + "api/User/";
static String cocRest = baseUrlServices + "COCWS.svc/REST/";
static String ssoAuthRedirection = ssoBaseUrl + "/api/auth/connect";
static String ssoAuthRedirection = "https://sso-uat.hmg.com/api/auth/connect";
//Chat
static String chatServerBaseUrl = "https://apiderichat.hmg.com/";
@ -63,7 +55,6 @@ class ApiConsts {
static String marathonBaseUrlUAT = "https://marathoon.com/uatservice/api/";
static String marathonBaseUrl = marathonBaseUrlLive;
// static String marathonBaseUrl = marathonBaseUrlUAT;
static String marathonBaseUrlServices = "https://marathoon.com/service/";
static String marathonParticipantLoginUrl = marathonBaseUrl + "auth/participantlogin";
@ -80,11 +71,6 @@ class ApiConsts {
//DummyCards for the UI
static int tabletMinLength = 500;
// Notification Center GUIDs for OTP
static String notificationCenterAppGUID = "VDZreDY5TTNmTDVudzc4aDkwU3ZqcW9OcmI0";
static String notificationCenterLoginServiceGUID = "D8BD5C76-4B50-4122-81D0-C3AA25DB08A2";
static String notificationCenterForgotPasswordServiceGUID = "5CCCC2C9-EEA0-4207-9BE9-EE1FBBFFE8D0";
}
class SharedPrefsConsts {
@ -100,3 +86,5 @@ class SharedPrefsConsts {
static String mohemmWifiPassword = "mohemmWifiPassword";
static String editItemForSale = "editItemForSale";
}

@ -22,7 +22,7 @@ class DateUtil {
return DateTime.now();
}
static DateTime convertStringToDate(String date) {
static DateTime convertStringToDate(String? date) {
// /Date(1585774800000+0300)/
if (date != null) {
const start = "/Date(";
@ -34,8 +34,9 @@ class DateUtil {
date.substring(startIndex + start.length, endIndex),
),
);
} else
} else {
return DateTime.now();
}
}
static DateTime convertSimpleStringDateToDate(String date, {bool isITG = false}) {
@ -524,47 +525,14 @@ class DateUtil {
// }
// return "";
// }
static String replaceArabicNumber(String? input) {
if (input == null || input.isEmpty) return input ?? "";
static String replaceArabicNumber(String input) {
const english = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const arabic = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
const persian = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
for (int i = 0; i < english.length; i++) {
input = input!.replaceAll(arabic[i], english[i]).replaceAll(persian[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 (_) {}
input = input.replaceAll(arabic[i], english[i]);
}
return null;
return input;
}
}

@ -20,7 +20,9 @@ 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/loading_dialog.dart';
import 'package:nfc_manager/nfc_manager.dart';
import 'package:nfc_manager/platform_tags.dart';
import 'package:nfc_manager/nfc_manager_android.dart';
import 'package:nfc_manager/nfc_manager_ios.dart';
// import 'package:nfc_manager/platform_tags.dart';
import 'package:shared_preferences/shared_preferences.dart';
// ignore_for_file: avoid_annotating_with_dynamic
@ -135,8 +137,8 @@ class Utils {
}
}
static Future showErrorDialog({required BuildContext context, required VoidCallback onOkTapped, required String message, VoidCallback? onCloseTap}) async {
return showDialog(context: context, builder: (BuildContext context) => ConfirmDialog(message: message, onTap: onOkTapped, onCloseTap: onCloseTap));
static Future showErrorDialog({required BuildContext context, required VoidCallback onOkTapped, required String message}) async {
return showDialog(context: context, builder: (BuildContext context) => ConfirmDialog(message: message, onTap: onOkTapped));
}
static void confirmDialog(cxt, String message, {VoidCallback? onTap}) {
@ -349,15 +351,34 @@ class Utils {
NfcManager.instance
.startSession(
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) {
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);
id = NfcTagAndroid.from(tag)?.id;
} else if (Platform.isIOS) {
id = MiFareIos.from(tag)?.identifier;
}
if (id == null) {
await NfcManager.instance.stopSession();
return;
}
String identifier = f.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
NfcManager.instance.stopSession();
String identifier =
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);
},
pollingOptions: {NfcPollingOption.iso14443},

@ -10,9 +10,7 @@ import 'package:mohem_flutter_app/ui/chat/favorite_users_screen.dart';
import 'package:mohem_flutter_app/ui/chat/group_chat_detaied_screen.dart';
import 'package:mohem_flutter_app/ui/chat/group_members.dart';
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_ovr_home.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/itg/change_itg_ad_password_screen.dart';
import 'package:mohem_flutter_app/ui/landing/itg/its_add_screen_video_image.dart';
@ -35,6 +33,7 @@ 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_listview_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_team/create_request.dart';
import 'package:mohem_flutter_app/ui/my_team/employee_details.dart';
@ -43,7 +42,6 @@ import 'package:mohem_flutter_app/ui/my_team/profile_details.dart';
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/view_attendance.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/performance/performance_appraisal_screen.dart';
import 'package:mohem_flutter_app/ui/profile/add_update_family_member.dart';
@ -58,7 +56,7 @@ import 'package:mohem_flutter_app/ui/profile/profile_screen.dart';
import 'package:mohem_flutter_app/ui/screens/announcements/announcement_details.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/courses/courses_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/event_activity/event_activity.dart';
@ -171,9 +169,6 @@ class AppRoutes {
static const String offersAndDiscounts = "/offersAndDiscounts";
static const String offersAndDiscountsDetails = "/offersAndDiscountsDetails";
// Courses
static const String courses = "/courses";
//Pay slip
static const String monthlyPaySlip = "/monthlyPaySlip";
@ -199,6 +194,7 @@ class AppRoutes {
static const String chat = "/chat";
static const String chatDetailed = "/chatDetailed";
static const String chatFavoriteUsers = "/chatFavoriteUsers";
static const String chatBotHome = "/chatBotHome";
//Group Chat
static const String manageGroup = "/manageGroup";
@ -219,13 +215,6 @@ class AppRoutes {
static const String activityScreen = "/activityScreen";
static const String etqanOvr = "/etqanOvr";
static const String etqanCreateRequest = "/etqanCreateRequest";
static const String etqanGetIncidientRequest = "/etqanGetIncidientRequest";
//Parking QR
static const String parkingQr = "/parkingQrScreen";
static final Map<String, WidgetBuilder> routes = {
login: (BuildContext context) => LoginScreen(),
verifyLogin: (BuildContext context) => VerifyLoginScreen(),
@ -238,8 +227,8 @@ class AppRoutes {
newPassword: (BuildContext context) => NewPasswordScreen(),
forgotPassword: (BuildContext context) => ForgotPasswordScreen(),
todayAttendance: (BuildContext context) => TodayAttendanceScreen2(),
//eit
//eit
addEitScreen: (BuildContext context) => AddEITScreen(),
//Work List
@ -250,7 +239,6 @@ class AppRoutes {
worklistSettings: (BuildContext context) => WorklistSettings(),
// Leave Balance
leaveBalance: (BuildContext context) => LeaveBalance(),
addLeaveBalance: (BuildContext context) => AddLeaveBalanceScreen(),
@ -306,9 +294,6 @@ class AppRoutes {
offersAndDiscounts: (BuildContext context) => OffersAndDiscountsHome(),
offersAndDiscountsDetails: (BuildContext context) => OffersAndDiscountsDetails(),
// Courses
courses: (BuildContext context) => const CoursesScreen(),
//pay slip
monthlyPaySlip: (BuildContext context) => MonthlyPaySlipScreen(),
performanceEvaluation: (BuildContext context) => PerformanceAppraisal(),
@ -330,6 +315,7 @@ class AppRoutes {
chat: (BuildContext context) => ChatHome(),
chatDetailed: (BuildContext context) => ChatDetailScreen(),
chatFavoriteUsers: (BuildContext context) => ChatFavoriteUsersScreen(),
chatBotHome: (BuildContext context) => ChatBotScreen(),
//Group Chat
manageGroup: (BuildContext context) => ManageGroupScreen(),
@ -346,16 +332,6 @@ class AppRoutes {
unsafeDeviceScreen: (BuildContext context) => const UnsafeDeviceScreen(),
appUpdateScreen: (BuildContext context) => const AppUpdateScreen(),
childEducation: (BuildContext context) => ChildEducationAssistance(),
activityScreen:(BuildContext context) => const EventActivityScreen(),
//ETQAN OVR
etqanOvr: (BuildContext context) => EtqanOvrHome(),
etqanCreateRequest: (BuildContext context) => EtqanOvrCreateRequest(),
etqanGetIncidientRequest: (BuildContext context) => EtqanOvrRequestDetailed(),
activityScreen:(BuildContext context) => const EventActivityScreen(),
//Parking QR
parkingQr: (BuildContext context) => const ParkingQrScreen(),
activityScreen: (BuildContext context) => const EventActivityScreen(),
};
}

@ -180,7 +180,6 @@ class CodegenLoader extends AssetLoader{
"quotationAnalysis": "تحليل الاقتباس",
"subject": "موضوعات",
"description": "وصف",
"pleaseEnterDescription": "الرجاء إدخال الوصف",
"supplier": "المورد",
"site": "موقع",
"buyer": "مشتر",
@ -275,7 +274,6 @@ class CodegenLoader extends AssetLoader{
"fullName": "الأسم الكامل",
"remove": "حذف",
"submit": "ارسال",
"submitAsAnonymous": "إرسال كمجهول",
"areYouSureYouWantToSubmit": "هل أنت متأكد أنك تريد أن تقدم؟",
"comments": "تعليقات",
"writeComment": "أكتب تعليقا",
@ -657,40 +655,16 @@ class CodegenLoader extends AssetLoader{
"yearsUsed": "سنوات الاستخدام",
"faRequest": "طلب التخلص من FA",
"showMore": "عرض المزيد",
"ticketDetails": "تفاصيل التذكرة",
"currentTicketBalance": "رصيد التذكرة الحالي",
"ticketHistory": "تاريخ التذكرة",
"proceedBooking": "المتابعة للحجز",
"courses": "الدورات التدريبية",
"course": "دورة",
"status": "الحالة",
"deadline": "الموعد النهائي",
"overdue": "متأخر",
"noCoursesAvailable": "لا توجد دورات متاحة",
"pullToRefresh": "اسحب للتحديث",
"courseUrlNotAvailable": "رابط الدورة غير متوفر",
"buyerDetails": "تفاصيل المشتري",
"etqanOVR": "الإبلاغ عن حادثة",
"etqanRequest": "طلبات الحوادث",
"selectProject": "حدد المشروع",
"project": "مشروع",
"keen": "حريص",
"reportIncident": "الإبلاغ عن الحادث",
"myIncidents": "حوادثي",
"incidentDetails": "تفاصيل الحادث",
"ticketNumber": "رقم التذكرة",
"ticketId": "معرف التذكرة",
"pleaseSelectProject": "الرجاء اختيار مشروع",
"viewAttachment": "عرض المرفق",
"parkingQr": "وقوف السيارات QR",
"startNow": "ابدأ الآن",
"destination_type": "نوع الوجهة",
"source_organization_name": "اسم منظمة المصدر",
"destination_organization_name": "اسم منظمة الوجهة",
"costCenter": "مركز التكلفة",
"priceSAR": "السعر (ريال سعودي)",
"amountSAR": "المبلغ (ريال سعودي)",
"amuLastThreeMonths": "AMU (آخر 3 أشهر)"
"chatBot": "المساعد الذكي",
"helloAskMe": "مرحباً، اسألني",
"anything": "عن أي شيء...",
"thinking": "جاري التفكير",
"waitingForResponse": "في انتظار الرد...",
"typeAMessage": "اكتب رسالة...",
"initializingChatPleaseWait": "يرجى الانتظار، جاري تهيئة المحادثة...",
"am": "ص",
"pm": "م"
};
static const Map<String,dynamic> _en_US = {
"mohemm": "Mohemm",
@ -852,7 +826,6 @@ static const Map<String,dynamic> _en_US = {
"subject": "Subject",
"quotationAnalysis": "Quotation Analysis",
"description": "Description",
"pleaseEnterDescription": "Please enter description",
"supplier": "Supplier",
"site": "Site",
"buyer": "Buyer",
@ -951,7 +924,6 @@ static const Map<String,dynamic> _en_US = {
"remove": "Remove",
"Attendance": "Attendance",
"submit": "Submit",
"submitAsAnonymous": "Submit as Anonymous",
"areYouSureYouWantToSubmit": "Are you sure you want to submit?",
"comments": "Comments",
"writeComment": "Write a comment",
@ -1335,40 +1307,16 @@ static const Map<String,dynamic> _en_US = {
"yearsUsed": "Years Used",
"faRequest": "FA Disposal Request",
"showMore": "Show More",
"ticketDetails": "Ticket Details",
"currentTicketBalance": "Current Ticket Balance",
"ticketHistory": "Ticket History",
"proceedBooking": "Proceed For Booking",
"courses": "Courses",
"course": "Course",
"status": "Status",
"deadline": "Deadline",
"overdue": "Overdue",
"noCoursesAvailable": "No Courses Available",
"pullToRefresh": "Pull to refresh",
"courseUrlNotAvailable": "Course URL not available",
"buyerDetails": "Buyer Details",
"etqanOVR": "Report An Incident",
"etqanRequest": "Incident Requests",
"selectProject": "Select Project",
"project": "Project",
"keen": "Keen",
"reportIncident": "Report Incident",
"myIncidents": "My Incidents",
"incidentDetails": "Incident Details",
"ticketNumber": "Ticket Number",
"ticketId": "Ticket ID",
"pleaseSelectProject": "Please select a project",
"viewAttachment": "View Attachment",
"parkingQr": "Parking QR",
"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)"
"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"
};
static const Map<String, Map<String,dynamic>> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US};
}

@ -167,7 +167,6 @@ abstract class LocaleKeys {
static const quotationAnalysis = 'quotationAnalysis';
static const subject = 'subject';
static const description = 'description';
static const pleaseEnterDescription = 'pleaseEnterDescription';
static const supplier = 'supplier';
static const site = 'site';
static const buyer = 'buyer';
@ -262,7 +261,6 @@ abstract class LocaleKeys {
static const fullName = 'fullName';
static const remove = 'remove';
static const submit = 'submit';
static const submitAsAnonymous = 'submitAsAnonymous';
static const areYouSureYouWantToSubmit = 'areYouSureYouWantToSubmit';
static const comments = 'comments';
static const writeComment = 'writeComment';
@ -323,8 +321,6 @@ abstract class LocaleKeys {
static const createdFor = 'createdFor';
static const requestCreatedSuccessfully = 'requestCreatedSuccessfully';
static const search = 'search';
static const filterEnterPrDetailsHere = 'filterEnterPrDetailsHere';
static const filterEnterPoDetailsHere = 'filterEnterPoDetailsHere';
static const wantToReject = 'wantToReject';
static const requestType = 'requestType';
static const employeeDigitalID = 'employeeDigitalID';
@ -631,39 +627,15 @@ abstract class LocaleKeys {
static const yearsUsed = 'yearsUsed';
static const faRequest = 'faRequest';
static const showMore = 'showMore';
static const ticketDetails = 'ticketDetails';
static const currentTicketBalance = 'currentTicketBalance';
static const ticketHistory = 'ticketHistory';
static const proceedBooking = 'proceedBooking';
static const courses = 'courses';
static const course = 'course';
static const status = 'status';
static const deadline = 'deadline';
static const overdue = 'overdue';
static const noCoursesAvailable = 'noCoursesAvailable';
static const pullToRefresh = 'pullToRefresh';
static const courseUrlNotAvailable = 'courseUrlNotAvailable';
static const buyerDetails = 'buyerDetails';
static const etqanOVR = 'etqanOVR';
static const etqanRequest = 'etqanRequest';
static const selectProject = 'selectProject';
static const project = 'project';
static const keen = 'keen';
static const reportIncident = 'reportIncident';
static const myIncidents = 'myIncidents';
static const incidentDetails = 'incidentDetails';
static const ticketNumber = 'ticketNumber';
static const ticketId = 'ticketId';
static const pleaseSelectProject = 'pleaseSelectProject';
static const viewAttachment = 'viewAttachment';
static const parkingQr = 'parkingQr';
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 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';
}

@ -8,7 +8,6 @@
import 'package:audio_session/audio_session_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_messaging_web/firebase_messaging_web.dart';
import 'package:fluttertoast/fluttertoast_web.dart';
@ -27,7 +26,6 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart';
void registerPlugins(Registrar registrar) {
AudioSessionWeb.registerWith(registrar);
CameraPlugin.registerWith(registrar);
FilePickerWeb.registerWith(registrar);
FirebaseCoreWeb.registerWith(registrar);
FirebaseMessagingWeb.registerWith(registrar);
FluttertoastWebPlugin.registerWith(registrar);

@ -12,13 +12,14 @@ import 'package:mohem_flutter_app/models/post_params_model.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/provider/eit_provider_model.dart';
import 'package:mohem_flutter_app/provider/etqan_ovr_provider.dart';
import 'package:mohem_flutter_app/theme/app_theme.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:month_year_picker/month_year_picker.dart';
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
import 'package:sizer/sizer.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb;
// test uat account
// username 199067
@ -54,7 +55,6 @@ Future<void> main() async {
ChangeNotifierProvider<EITProviderModel>(create: (_) => EITProviderModel()),
ChangeNotifierProvider<ChatProviderModel>(create: (_) => ChatProviderModel()),
ChangeNotifierProvider<MarathonProvider>(create: (_) => MarathonProvider()),
ChangeNotifierProvider<EtqanOvrProviderModel>(create: (_) => EtqanOvrProviderModel()),
// ChangeNotifierProvider<ChatCallProvider>(
// create: (_) => ChatCallProvider(),
// ),
@ -77,32 +77,33 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Sizer(
builder: (BuildContext context, Orientation orientation, DeviceType deviceType) {
builder: (BuildContext context, Orientation orientation, ScreenType deviceType){
PostParamsModel? obj = AppState().postParamsObject;
obj?.languageID = EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2;
AppState().setPostParamsModel(obj!);
List<LocalizationsDelegate<dynamic>> delegates = context.localizationDelegates;
// delegates.add(GlobalMaterialLocalizations.delegate);
delegates.add(MonthYearPickerLocalizations.delegate);
return SafeArea(
top: false, // Set to true if you want to avoid the notch area as well
bottom: Platform.isIOS ? false : true,
child: MaterialApp(
navigatorKey: AppRoutes.navigatorKey,
builder: (BuildContext context, Widget? child) {
return MediaQuery(data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), child: child!);
},
theme: AppTheme.getTheme(EasyLocalization.of(context)?.locale.languageCode == "ar"),
debugShowCheckedModeBanner: false,
localizationsDelegates: delegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
initialRoute: AppRoutes.initialRoute,
routes: AppRoutes.routes,
),
);
},
obj?.languageID = EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2;
AppState().setPostParamsModel(obj!);
List<LocalizationsDelegate<dynamic>> delegates = context.localizationDelegates;
// delegates.add(GlobalMaterialLocalizations.delegate);
delegates.add(MonthYearPickerLocalizations.delegate);
return SafeArea(
top: false, // Set to true if you want to avoid the notch area as well
bottom: Platform.isIOS ? false : true,
child: MaterialApp(
navigatorKey: AppRoutes.navigatorKey,
builder: (BuildContext context, Widget? child) {
return MediaQuery(data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), child: child!);
},
theme: AppTheme.getTheme(EasyLocalization.of(context)?.locale.languageCode == "ar"),
debugShowCheckedModeBanner: false,
localizationsDelegates: delegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
initialRoute: AppRoutes.initialRoute,
routes: AppRoutes.routes,
),
);
}
);
}
}

@ -1,94 +0,0 @@
class CoursesResponseModel {
bool? success;
int? status;
String? username;
List<CourseStatus>? coursesStatus;
int? totalCourses;
CoursesResponseModel({
this.success,
this.status,
this.username,
this.coursesStatus,
this.totalCourses,
});
CoursesResponseModel.fromJson(Map<String, dynamic> json) {
success = json['success'];
status = json['status'];
username = json['username'];
if (json['courses_status'] != null) {
coursesStatus = <CourseStatus>[];
json['courses_status'].forEach((v) {
coursesStatus!.add(CourseStatus.fromJson(v));
});
}
totalCourses = json['total_courses'];
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{};
data['success'] = success;
data['status'] = status;
data['username'] = username;
if (coursesStatus != null) {
data['courses_status'] = coursesStatus!.map((v) => v.toJson()).toList();
}
data['total_courses'] = totalCourses;
return data;
}
}
class CourseStatus {
String? cId;
String? cFullname;
String? cNameAr;
String? cShortname;
String? cStatus;
String? cIsMohemmAffective;
String? cStartdate;
String? cEnddate;
String? cUrl;
String? token;
CourseStatus({
this.cId,
this.cFullname,
this.cNameAr,
this.cShortname,
this.cStatus,
this.cIsMohemmAffective,
this.cStartdate,
this.cEnddate,
this.cUrl,
this.token
});
CourseStatus.fromJson(Map<String, dynamic> json) {
cId = json['c_id'];
cFullname = json['c_fullname'];
cNameAr = json['c_name_ar'];
cShortname = json['c_shortname'];
cStatus = json['c_status'];
cIsMohemmAffective = json['c_is_mohemm_affective'];
cStartdate = json['c_startdate'];
cEnddate = json['c_enddate'];
cUrl = json['c_url'];
token =json['c_token'];
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{};
data['c_id'] = cId;
data['c_fullname'] = cFullname;
data['c_name_ar'] = cNameAr;
data['c_shortname'] = cShortname;
data['c_status'] = cStatus;
data['c_is_mohemm_affective'] = cIsMohemmAffective;
data['c_startdate'] = cStartdate;
data['c_enddate'] = cEnddate;
data['c_url'] = cUrl;
data['c_token'] = token;
return data;
}
}

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

@ -30,7 +30,7 @@ class CollectionTransaction {
String? dISPLAYFLAG;
int? fROMROWNUM;
int? nOOFROWS;
dynamic nUMBERVALUE;
dynamic? nUMBERVALUE;
int? rOWNUM;
String? sEGMENTNAME;
String? sEGMENTPROMPT;
@ -80,7 +80,7 @@ class CollectionTransaction {
}
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['DATATYPE'] = this.dATATYPE;
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,
};
}

@ -1,57 +0,0 @@
import 'dart:convert';
class EtqanGetEmployeeIncidentRequestModel {
List<EtqanGetIncidentRequestResponse>? etqanGetIncidentRequestResponse;
EtqanGetEmployeeIncidentRequestModel({
this.etqanGetIncidentRequestResponse,
});
factory EtqanGetEmployeeIncidentRequestModel.fromRawJson(String str) => EtqanGetEmployeeIncidentRequestModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory EtqanGetEmployeeIncidentRequestModel.fromJson(Map<String, dynamic> json) => EtqanGetEmployeeIncidentRequestModel(
etqanGetIncidentRequestResponse: json["ETQAN_GetIncidentRequest_Response"] == null ? [] : List<EtqanGetIncidentRequestResponse>.from(json["ETQAN_GetIncidentRequest_Response"]!.map((x) => EtqanGetIncidentRequestResponse.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"ETQAN_GetIncidentRequest_Response": etqanGetIncidentRequestResponse == null ? [] : List<dynamic>.from(etqanGetIncidentRequestResponse!.map((x) => x.toJson())),
};
}
class EtqanGetIncidentRequestResponse {
String? key;
String? keyAr;
String? value;
String? valueAr;
List<String>? subValues;
EtqanGetIncidentRequestResponse({
this.key,
this.keyAr,
this.value,
this.valueAr,
this.subValues,
});
factory EtqanGetIncidentRequestResponse.fromRawJson(String str) => EtqanGetIncidentRequestResponse.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory EtqanGetIncidentRequestResponse.fromJson(Map<String, dynamic> json) => EtqanGetIncidentRequestResponse(
key: json["key"],
keyAr: json["keyAr"],
value: json["value"],
valueAr: json["valueAr"],
subValues: json["subValues"] == null ? [] : List<String>.from(json["subValues"]!.map((x) => x)),
);
Map<String, dynamic> toJson() => {
"key": key,
"keyAr": keyAr,
"value": value,
"valueAr": valueAr,
"subValues": subValues == null ? [] : List<dynamic>.from(subValues!.map((x) => x)),
};
}

@ -1,65 +0,0 @@
import 'dart:convert';
class EtqanGetEmployeeRequestsModel {
List<EtqanGetEmployeeOvrRequestsResponse>? etqanGetEmployeeOvrRequestsResponse;
EtqanGetEmployeeRequestsModel({
this.etqanGetEmployeeOvrRequestsResponse,
});
factory EtqanGetEmployeeRequestsModel.fromRawJson(String str) => EtqanGetEmployeeRequestsModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory EtqanGetEmployeeRequestsModel.fromJson(Map<String, dynamic> json) => EtqanGetEmployeeRequestsModel(
etqanGetEmployeeOvrRequestsResponse: json["ETQAN_GetEmployeeOVRRequests_Response"] == null ? [] : List<EtqanGetEmployeeOvrRequestsResponse>.from(json["ETQAN_GetEmployeeOVRRequests_Response"]!.map((x) => EtqanGetEmployeeOvrRequestsResponse.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"ETQAN_GetEmployeeOVRRequests_Response": etqanGetEmployeeOvrRequestsResponse == null ? [] : List<dynamic>.from(etqanGetEmployeeOvrRequestsResponse!.map((x) => x.toJson())),
};
}
class EtqanGetEmployeeOvrRequestsResponse {
String? createdDate;
String? description;
String? id;
bool? isAnonymous;
String? status;
String? statusAr;
String? ticketNumber;
EtqanGetEmployeeOvrRequestsResponse({
this.createdDate,
this.description,
this.id,
this.isAnonymous,
this.status,
this.statusAr,
this.ticketNumber,
});
factory EtqanGetEmployeeOvrRequestsResponse.fromRawJson(String str) => EtqanGetEmployeeOvrRequestsResponse.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory EtqanGetEmployeeOvrRequestsResponse.fromJson(Map<String, dynamic> json) => EtqanGetEmployeeOvrRequestsResponse(
createdDate: json["createdDate"],
description: json["description"],
id: json["id"],
isAnonymous: json["isAnonymous"],
status: json["status"],
statusAr: json["statusAr"],
ticketNumber: json["ticketNumber"],
);
Map<String, dynamic> toJson() => {
"createdDate": createdDate,
"description": description,
"id": id,
"isAnonymous": isAnonymous,
"status": status,
"statusAr": statusAr,
"ticketNumber": ticketNumber,
};
}

@ -1,53 +0,0 @@
import 'dart:convert';
class EtqanGetProjectsModel {
List<EtqanGetProjectsResponse>? etqanGetProjectsResponse;
EtqanGetProjectsModel({
this.etqanGetProjectsResponse,
});
factory EtqanGetProjectsModel.fromRawJson(String str) => EtqanGetProjectsModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory EtqanGetProjectsModel.fromJson(Map<String, dynamic> json) => EtqanGetProjectsModel(
etqanGetProjectsResponse: json["ETQAN_GetProjects_Response"] == null ? [] : List<EtqanGetProjectsResponse>.from(json["ETQAN_GetProjects_Response"]!.map((x) => EtqanGetProjectsResponse.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"ETQAN_GetProjects_Response": etqanGetProjectsResponse == null ? [] : List<dynamic>.from(etqanGetProjectsResponse!.map((x) => x.toJson())),
};
}
class EtqanGetProjectsResponse {
String? projectAr;
String? projectCode;
String? projectEn;
int? projectId;
EtqanGetProjectsResponse({
this.projectAr,
this.projectCode,
this.projectEn,
this.projectId,
});
factory EtqanGetProjectsResponse.fromRawJson(String str) => EtqanGetProjectsResponse.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory EtqanGetProjectsResponse.fromJson(Map<String, dynamic> json) => EtqanGetProjectsResponse(
projectAr: json["projectAr"],
projectCode: json["projectCode"],
projectEn: json["projectEn"],
projectId: json["projectId"],
);
Map<String, dynamic> toJson() => {
"projectAr": projectAr,
"projectCode": projectCode,
"projectEn": projectEn,
"projectId": projectId,
};
}

@ -398,7 +398,9 @@ class GenericResponseModel {
bool? isActiveCode;
bool? isSMSSent;
PortalDirectionData? portalDirectionData;
String? Mohemm_ENSAT_GetItgToken_Response;
AtlasInitiateChatResponse? atlasInitiateChatResponse;
AtlasContinueChatResponseDetails? atlasContinueChatResponseDetails;
GenericResponseModel({
this.date,
@ -671,7 +673,9 @@ class GenericResponseModel {
this.isActiveCode,
this.isSMSSent,
this.getFADisposalNtfDetails,
this.Mohemm_ENSAT_GetItgToken_Response,
this.atlasInitiateChatResponse,
this.atlasContinueChatResponseDetails,
});
GenericResponseModel.fromJson(Map<String, dynamic> json) {
@ -1465,7 +1469,10 @@ class GenericResponseModel {
ePharmacyGetItemOnHandList = json['ePharmacy_GetItemOnHandList'];
isActiveCode = json['isActiveCode'];
isSMSSent = json['isSMSSent'];
Mohemm_ENSAT_GetItgToken_Response = json['Mohemm_ENSAT_GetItgToken_Response'];
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"]);
}
Map<String, dynamic> toJson() {
@ -1951,7 +1958,9 @@ class GenericResponseModel {
data['ePharmacy_GetItemOnHandList'] = this.ePharmacyGetItemOnHandList;
data['isActiveCode'] = this.isActiveCode;
data['isSMSSent'] = this.isSMSSent;
data['Mohemm_ENSAT_GetItgToken_Response'] = this.Mohemm_ENSAT_GetItgToken_Response;
data["Atlas_InitiateChat_Response"] = atlasInitiateChatResponse?.toJson();
data["Atlas_ContinueChat_ResponseDetails"] = atlasContinueChatResponseDetails?.toJson();
return data;
}
}
@ -1977,3 +1986,138 @@ class TicketBookingResult {
TicketBookingResult(this.success, this.clientId);
}
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,
};
}

@ -22,8 +22,6 @@ class GetDayHoursTypeDetailsList {
dynamic? fROMROWNUM;
String? lATEINFLAG;
String? lATEINHRS;
String? lEAVEDESCRIPTION;
String? lEAVETYPECODE;
String? mISSINGSWIPEFLAG;
String? nONSCHEDULEDFLAG;
dynamic? nOOFROWS;
@ -68,8 +66,6 @@ class GetDayHoursTypeDetailsList {
this.fROMROWNUM,
this.lATEINFLAG,
this.lATEINHRS,
this.lEAVEDESCRIPTION,
this.lEAVETYPECODE,
this.mISSINGSWIPEFLAG,
this.nONSCHEDULEDFLAG,
this.nOOFROWS,
@ -114,8 +110,6 @@ class GetDayHoursTypeDetailsList {
fROMROWNUM = json['FROM_ROW_NUM'];
lATEINFLAG = json['LATE_IN_FLAG'];
lATEINHRS = json['LATE_IN_HRS'];
lEAVEDESCRIPTION = json['LEAVE_DESCRIPTION'];
lEAVETYPECODE = json['LEAVE_TYPE_CODE'];
mISSINGSWIPEFLAG = json['MISSING_SWIPE_FLAG'];
nONSCHEDULEDFLAG = json['NON_SCHEDULED_FLAG'];
nOOFROWS = json['NO_OF_ROWS'];
@ -162,8 +156,6 @@ class GetDayHoursTypeDetailsList {
data['FROM_ROW_NUM'] = this.fROMROWNUM;
data['LATE_IN_FLAG'] = this.lATEINFLAG;
data['LATE_IN_HRS'] = this.lATEINHRS;
data['LEAVE_DESCRIPTION'] = this.lEAVEDESCRIPTION;
data['LEAVE_TYPE_CODE'] = this.lEAVETYPECODE;
data['MISSING_SWIPE_FLAG'] = this.mISSINGSWIPEFLAG;
data['NON_SCHEDULED_FLAG'] = this.nONSCHEDULEDFLAG;
data['NO_OF_ROWS'] = this.nOOFROWS;

@ -1,61 +0,0 @@
import 'dart:convert';
class GetEmployeeParking {
GetEmployeeParkingClass? getEmployeeParking;
GetEmployeeParking({
this.getEmployeeParking,
});
factory GetEmployeeParking.fromRawJson(String str) => GetEmployeeParking.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory GetEmployeeParking.fromJson(Map<String, dynamic> json) => GetEmployeeParking(
getEmployeeParking: json["GET_Employee_Parking"] == null ? null : GetEmployeeParkingClass.fromJson(json["GET_Employee_Parking"]),
);
Map<String, dynamic> toJson() => {
"GET_Employee_Parking": getEmployeeParking?.toJson(),
};
}
class GetEmployeeParkingClass {
DateTime? createdAt;
String? employeeCode;
String? isNewQrCode;
String? qrCode;
String? qrCodeImageBase64;
bool? success;
GetEmployeeParkingClass({
this.createdAt,
this.employeeCode,
this.isNewQrCode,
this.qrCode,
this.qrCodeImageBase64,
this.success,
});
factory GetEmployeeParkingClass.fromRawJson(String str) => GetEmployeeParkingClass.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory GetEmployeeParkingClass.fromJson(Map<String, dynamic> json) => GetEmployeeParkingClass(
createdAt: json["createdAt"] == null ? null : DateTime.parse(json["createdAt"]),
employeeCode: json["employeeCode"],
isNewQrCode: json["isNewQRCode"],
qrCode: json["qrCode"],
qrCodeImageBase64: json["qrCodeImageBase64"],
success: json["success"],
);
Map<String, dynamic> toJson() => {
"createdAt": createdAt?.toIso8601String(),
"employeeCode": employeeCode,
"isNewQRCode": isNewQrCode,
"qrCode": qrCode,
"qrCodeImageBase64": qrCodeImageBase64,
"success": success,
};
}

@ -1,6 +1,6 @@
class GetMobileLoginInfoListModel {
int? iD;
int? employeeID;
String? employeeID;
int? channelID;
int? companyID;
String? deviceType;

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

@ -1,142 +0,0 @@
import 'dart:convert';
class GetGreetingCardsModelResponse {
List<GetGreetingCardsModelResponseElement>? getGreetingCardsModelResponse;
GetGreetingCardsModelResponse({
this.getGreetingCardsModelResponse,
});
factory GetGreetingCardsModelResponse.fromRawJson(String str) => GetGreetingCardsModelResponse.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory GetGreetingCardsModelResponse.fromJson(Map<String, dynamic> json) => GetGreetingCardsModelResponse(
getGreetingCardsModelResponse: json["Get_GreetingCardsModel_Response"] == null ? [] : List<GetGreetingCardsModelResponseElement>.from(json["Get_GreetingCardsModel_Response"]!.map((x) => GetGreetingCardsModelResponseElement.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"Get_GreetingCardsModel_Response": getGreetingCardsModelResponse == null ? [] : List<dynamic>.from(getGreetingCardsModelResponse!.map((x) => x.toJson())),
};
}
class GetGreetingCardsModelResponseElement {
String? backgroundImageUrlAr;
String? backgroundImageUrlEn;
String? buttonBgColor;
String? buttonText;
String? buttonTextN;
int? cardType;
int? categoryId;
String? categoryNameAr;
String? categoryNameEn;
int? channel;
String? createdOn;
String? descriptionAr;
String? descriptionEn;
String? endDate;
bool? hasButton;
int? id;
bool? isActive;
int? moduleId;
String? moduleName;
String? moduleNameN;
String? startDate;
String? textColor;
String? titleAr;
String? titleEn;
String? urlAr;
String? urlEn;
GetGreetingCardsModelResponseElement({
this.backgroundImageUrlAr,
this.backgroundImageUrlEn,
this.buttonBgColor,
this.buttonText,
this.buttonTextN,
this.cardType,
this.categoryId,
this.categoryNameAr,
this.categoryNameEn,
this.channel,
this.createdOn,
this.descriptionAr,
this.descriptionEn,
this.endDate,
this.hasButton,
this.id,
this.isActive,
this.moduleId,
this.moduleName,
this.moduleNameN,
this.startDate,
this.textColor,
this.titleAr,
this.titleEn,
this.urlAr,
this.urlEn,
});
factory GetGreetingCardsModelResponseElement.fromRawJson(String str) => GetGreetingCardsModelResponseElement.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory GetGreetingCardsModelResponseElement.fromJson(Map<String, dynamic> json) => GetGreetingCardsModelResponseElement(
backgroundImageUrlAr: json["BackgroundImageUrlAr"],
backgroundImageUrlEn: json["BackgroundImageUrlEn"],
buttonBgColor: json["ButtonBgColor"],
buttonText: json["ButtonText"],
buttonTextN: json["ButtonTextN"],
cardType: json["CardType"],
categoryId: json["CategoryID"],
categoryNameAr: json["CategoryNameAr"],
categoryNameEn: json["CategoryNameEn"],
channel: json["Channel"],
createdOn: json["CreatedOn"],
descriptionAr: json["DescriptionAr"],
descriptionEn: json["DescriptionEn"],
endDate: json["EndDate"],
hasButton: json["HasButton"],
id: json["ID"],
isActive: json["IsActive"],
moduleId: json["ModuleID"],
moduleName: json["ModuleName"],
moduleNameN: json["ModuleNameN"],
startDate: json["StartDate"],
textColor: json["TextColor"],
titleAr: json["TitleAr"],
titleEn: json["TitleEn"],
urlAr: json["UrlAr"],
urlEn: json["UrlEn"],
);
Map<String, dynamic> toJson() => {
"BackgroundImageUrlAr": backgroundImageUrlAr,
"BackgroundImageUrlEn": backgroundImageUrlEn,
"ButtonBgColor": buttonBgColor,
"ButtonText": buttonText,
"ButtonTextN": buttonTextN,
"CardType": cardType,
"CategoryID": categoryId,
"CategoryNameAr": categoryNameAr,
"CategoryNameEn": categoryNameEn,
"Channel": channel,
"CreatedOn": createdOn,
"DescriptionAr": descriptionAr,
"DescriptionEn": descriptionEn,
"EndDate": endDate,
"HasButton": hasButton,
"ID": id,
"IsActive": isActive,
"ModuleID": moduleId,
"ModuleName": moduleName,
"ModuleNameN": moduleNameN,
"StartDate": startDate,
"TextColor": textColor,
"TitleAr": titleAr,
"TitleEn": titleEn,
"UrlAr": urlAr,
"UrlEn": urlEn,
};
}

@ -136,21 +136,10 @@ class ActionButtonsColl {
late String btnTextEn;
late String btnTextAr;
late String actionValue;
dynamic iconOrImage;
String? actionType;
late dynamic iconOrImage;
late int orderNo;
String? url;
ActionButtonsColl({
required this.actionButtonId,
required this.btnTextEn,
required this.btnTextAr,
required this.actionValue,
this.iconOrImage,
this.actionType,
required this.orderNo,
this.url,
});
ActionButtonsColl({required this.actionButtonId, required this.btnTextEn, required this.btnTextAr, required this.actionValue, required this.iconOrImage, required this.orderNo});
ActionButtonsColl.fromJson(Map<String, dynamic> json) {
actionButtonId = json['actionButtonId'];
@ -158,9 +147,7 @@ class ActionButtonsColl {
btnTextAr = json['btnTextAr'];
actionValue = json['actionValue'];
iconOrImage = json['iconOrImage'];
actionType = json['actionType'];
orderNo = json['orderNo'];
url = json['url'];
}
Map<String, dynamic> toJson() {
@ -170,9 +157,7 @@ class ActionButtonsColl {
data['btnTextAr'] = this.btnTextAr;
data['actionValue'] = this.actionValue;
data['iconOrImage'] = this.iconOrImage;
data['actionType'] = this.actionType;
data['orderNo'] = this.orderNo;
data['url'] = this.url;
return data;
}
}

@ -12,7 +12,12 @@ MohemmItgResponseItem mohemmItgResponseItemFromJson(String str) => MohemmItgResp
String mohemmItgResponseItemToJson(MohemmItgResponseItem data) => json.encode(data.toJson());
class MohemmItgResponseItem {
MohemmItgResponseItem({this.statusCode, this.message, this.originalErrMsg, this.result});
MohemmItgResponseItem({
this.statusCode,
this.message,
this.originalErrMsg,
this.result,
});
final int? statusCode;
final dynamic? message;
@ -20,64 +25,77 @@ class MohemmItgResponseItem {
final ItgResponseResult? result;
factory MohemmItgResponseItem.fromJson(Map<String, dynamic> json) => MohemmItgResponseItem(
statusCode: json["statusCode"] == null ? null : json["statusCode"],
message: json["message"],
originalErrMsg: json["originalErrMsg"],
result: json["result"] == null ? null : ItgResponseResult.fromJson(json["result"]),
);
statusCode: json["statusCode"] == null ? null : json["statusCode"],
message: json["message"],
originalErrMsg: json["originalErrMsg"],
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 {
ItgResponseResult({this.totalItemsCount, this.data, this.errormsg});
//
// final dynamic totalItemsCount;
// final ItgResponseData? data;
// final dynamic 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!.toJson(),
// "errormsg": errormsg,
// };
// }
// todo these lines, for ERM new change on UAT
dynamic totalItemsCount;
List<ItgResponseData>? data;
dynamic errormsg;
ItgResponseResult.fromJson(Map<String, dynamic> json) {
totalItemsCount = json['totalItemsCount'];
if (json['data'] != null) {
data = [];
if (json['data'] is List) {
json['data'].forEach((v) {
data!.add(ItgResponseData.fromJson(v));
});
} else {
data!.add(ItgResponseData.fromJson(json['data']));
}
ItgResponseResult({
this.totalItemsCount,
this.data,
this.errormsg,
});
//
// final dynamic totalItemsCount;
// final ItgResponseData? data;
// final dynamic 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!.toJson(),
// "errormsg": errormsg,
// };
// }
// todo these lines, for ERM new change on UAT
dynamic totalItemsCount;
List<ItgResponseData>? data;
dynamic errormsg;
ItgResponseResult.fromJson(Map<String, dynamic> json) {
totalItemsCount = json['totalItemsCount'];
if (json['data'] != null) {
data = [];
if(json['data'] is List) {
json['data'].forEach((v) {
data!.add(ItgResponseData.fromJson(v));
});
} else {
data!.add(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"]),
// errormsg: json["errormsg"],
// );
}
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 {
@ -103,7 +121,6 @@ class ItgResponseData {
this.pageSize,
this.pageNo,
this.languageId,
this.deadline,
});
final String? notificationMasterId;
@ -127,55 +144,52 @@ class ItgResponseData {
final dynamic pageSize;
final dynamic pageNo;
final dynamic languageId;
dynamic deadline;
factory ItgResponseData.fromJson(Map<String, dynamic> json) => ItgResponseData(
notificationMasterId: json["notificationMasterId"] == null ? null : json["notificationMasterId"],
notificationType: json["notificationType"] == null ? null : json["notificationType"],
referenceItemId: json["referenceItemId"] == null ? null : json["referenceItemId"],
notificationTitle: json["notificationTitle"] == null ? null : json["notificationTitle"],
enableAt: json["enableAt"] == null ? null : json["enableAt"],
applicationItemId: json["applicationItemId"],
startDate: json["startDate"],
endDate: json["endDate"],
deadline: json["deadline"] == null ? null : json["deadline"],
isRepeat: json["isRepeat"] == null ? null : json["isRepeat"],
channelId: json["channelId"] == null ? null : json["channelId"],
serviceId: json["serviceId"] == null ? null : json["serviceId"],
channelName: json["channelName"] == null ? null : json["channelName"],
serviceName: json["serviceName"] == null ? null : json["serviceName"],
isDeleted: json["isDeleted"] == null ? null : json["isDeleted"],
showDelete: json["showDelete"] == null ? null : json["showDelete"],
advertisement: json["advertisement"] == null ? null : Advertisement.fromJson(json["advertisement"]),
survey: json["survey"] == null ? null : SurveyModel.fromJson(json["survey"]),
isActive: json["isActive"],
pageSize: json["pageSize"],
pageNo: json["pageNo"],
languageId: json["languageId"],
);
notificationMasterId: json["notificationMasterId"] == null ? null : json["notificationMasterId"],
notificationType: json["notificationType"] == null ? null : json["notificationType"],
referenceItemId: json["referenceItemId"] == null ? null : json["referenceItemId"],
notificationTitle: json["notificationTitle"] == null ? null : json["notificationTitle"],
enableAt: json["enableAt"] == null ? null : json["enableAt"],
applicationItemId: json["applicationItemId"],
startDate: json["startDate"],
endDate: json["endDate"],
isRepeat: json["isRepeat"] == null ? null : json["isRepeat"],
channelId: json["channelId"] == null ? null : json["channelId"],
serviceId: json["serviceId"] == null ? null : json["serviceId"],
channelName: json["channelName"] == null ? null : json["channelName"],
serviceName: json["serviceName"] == null ? null : json["serviceName"],
isDeleted: json["isDeleted"] == null ? null : json["isDeleted"],
showDelete: json["showDelete"] == null ? null : json["showDelete"],
advertisement: json["advertisement"] == null ? null : Advertisement.fromJson(json["advertisement"]),
survey: json["survey"] == null ? null : SurveyModel.fromJson(json["survey"]),
isActive: json["isActive"],
pageSize: json["pageSize"],
pageNo: json["pageNo"],
languageId: json["languageId"],
);
Map<String, dynamic> toJson() => {
"notificationMasterId": notificationMasterId == null ? null : notificationMasterId,
"notificationType": notificationType == null ? null : notificationType,
"referenceItemId": referenceItemId == null ? null : referenceItemId,
"notificationTitle": notificationTitle == null ? null : notificationTitle,
"enableAt": enableAt == null ? null : enableAt,
"applicationItemId": applicationItemId,
"startDate": startDate,
"endDate": endDate,
"deadline": deadline == null ? null : deadline ,
"isRepeat": isRepeat == null ? null : isRepeat,
"channelId": channelId == null ? null : channelId,
"serviceId": serviceId == null ? null : serviceId,
"channelName": channelName == null ? null : channelName,
"serviceName": serviceName == null ? null : serviceName,
"isDeleted": isDeleted == null ? null : isDeleted,
"showDelete": showDelete == null ? null : showDelete,
"advertisement": advertisement,
"survey": survey,
"isActive": isActive,
"pageSize": pageSize,
"pageNo": pageNo,
"languageId": languageId,
};
"notificationMasterId": notificationMasterId == null ? null : notificationMasterId,
"notificationType": notificationType == null ? null : notificationType,
"referenceItemId": referenceItemId == null ? null : referenceItemId,
"notificationTitle": notificationTitle == null ? null : notificationTitle,
"enableAt": enableAt == null ? null : enableAt,
"applicationItemId": applicationItemId,
"startDate": startDate,
"endDate": endDate,
"isRepeat": isRepeat == null ? null : isRepeat,
"channelId": channelId == null ? null : channelId,
"serviceId": serviceId == null ? null : serviceId,
"channelName": channelName == null ? null : channelName,
"serviceName": serviceName == null ? null : serviceName,
"isDeleted": isDeleted == null ? null : isDeleted,
"showDelete": showDelete == null ? null : showDelete,
"advertisement": advertisement,
"survey": survey,
"isActive": isActive,
"pageSize": pageSize,
"pageNo": pageNo,
"languageId": languageId,
};
}

@ -1609,13 +1609,13 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
await File('$dirPath/.nomedia').create();
}
path = "$dirPath/${AppState().chatDetails!.response!.id}-$receiverID-${DateTime.now().microsecondsSinceEpoch}.aac";
recorderController = RecorderController()
..androidEncoder = AndroidEncoder.aac
..androidOutputFormat = AndroidOutputFormat.mpeg4
..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC
..sampleRate = 6000
..updateFrequency = const Duration(milliseconds: 100)
..bitRate = 18000;
recorderController = RecorderController();
// ..androidEncoder = AndroidEncoder.aac
// ..androidOutputFormat = AndroidOutputFormat.mpeg4
// ..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC
// ..sampleRate = 6000
// ..updateFrequency = const Duration(milliseconds: 100)
// ..bitRate = 18000;
playerController = PlayerController();
}

@ -19,11 +19,8 @@ import 'package:mohem_flutter_app/models/dashboard/list_menu.dart';
import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart';
import 'package:mohem_flutter_app/models/dashboard/menus.dart';
import 'package:mohem_flutter_app/models/dashboard/mohemm_itg_pending_task_responseitem.dart';
import 'package:mohem_flutter_app/models/dashboard/courses_response_model.dart';
import 'package:mohem_flutter_app/models/eit/get_eit_transaction_model.dart';
import 'package:mohem_flutter_app/models/eit_attachment_ticket_model.dart';
import 'package:mohem_flutter_app/models/generic_response_model.dart';
import 'package:mohem_flutter_app/models/greetings/greeting_card_model.dart';
import 'package:mohem_flutter_app/models/itg/itg_response_model.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart';
import 'package:mohem_flutter_app/models/sso_auth_model.dart';
@ -70,15 +67,6 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
bool isOffersLoading = true;
List<OffersListModel> getOffersList = [];
//Courses
bool isCoursesLoading = true;
CoursesResponseModel? coursesResponse;
List<CourseStatus> coursesList = [];
bool isGreetingCardsLoading = true;
bool isDisplayEidGreetings = false;
List<GetGreetingCardsModelResponseElement>? greetingCardsList;
//Attendance Tracking API's & Methods
Future<bool> fetchAttendanceTracking(context) async {
try {
@ -127,18 +115,12 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
getMenuEntriesList = null;
isOffersLoading = true;
getOffersList = [];
isGreetingCardsLoading = true;
isDisplayEidGreetings = false;
drawerMenuItemList = [
DrawerMenuItem("assets/images/drawer/my_profile.svg", LocaleKeys.myProfile.tr(), AppRoutes.profile),
DrawerMenuItem("assets/images/drawer/performance_evaluation.svg", LocaleKeys.performanceEvaluation.tr(), AppRoutes.performanceEvaluation),
DrawerMenuItem("assets/images/drawer/mowadhafi.svg", LocaleKeys.mowadhafhi.tr(), AppRoutes.mowadhafhi),
// DrawerMenuItem("assets/images/drawer/mowadhafi.svg", LocaleKeys.etqanOVR.tr(), AppRoutes.etqanOvr),
DrawerMenuItem("assets/images/drawer/mowadhafi.svg", LocaleKeys.keen.tr(), AppRoutes.etqanOvr),
DrawerMenuItem("assets/images/drawer/car_parking_icon.svg", LocaleKeys.parkingQr.tr(), AppRoutes.parkingQr),
DrawerMenuItem("assets/images/drawer/pending_trasactions.svg", LocaleKeys.pendingTransactions.tr(), AppRoutes.pendingTransactions),
DrawerMenuItem("assets/images/drawer/courses.svg", "courses".tr(), AppRoutes.courses),
// DrawerMenuItem("assets/images/drawer/drawer_marathon.svg", LocaleKeys.brainMarathon.tr(), AppRoutes.marathonIntroScreen),
DrawerMenuItem("assets/images/drawer/change_password.svg", LocaleKeys.changePassword.tr(), AppRoutes.changePassword),
];
@ -324,33 +306,10 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
}
void fetchGreetingCards() async {
isGreetingCardsLoading = true;
notifyListeners();
try {
GetGreetingCardsModelResponse? response = await DashboardApiClient().getGreetingCards();
greetingCardsList = response?.getGreetingCardsModelResponse ?? [];
if (greetingCardsList != null && greetingCardsList!.isNotEmpty) {
isDisplayEidGreetings = greetingCardsList!.any((card) => card.isActive == true);
} else {
isDisplayEidGreetings = false;
}
isGreetingCardsLoading = false;
notifyListeners();
} catch (ex) {
logger.wtf(ex);
isDisplayEidGreetings = false;
greetingCardsList = [];
isGreetingCardsLoading = false;
notifyListeners();
}
}
Future<void> fetchTicketBooking() async {
try {
GenericResponseModel? genericResponseModel = await DashboardApiClient().getTicketBookingRedirection();
if (genericResponseModel?.portalDirectionData?.pRedirection!.toLowerCase() == "alma") {
// ticketBookingResponse = TicketBookingResult(true, "a9f4d1a0596d4aea8f830992ec4bdac1");
ticketBookingResponse = TicketBookingResult(true, genericResponseModel?.portalDirectionData?.clientID);
return;
}
@ -360,13 +319,15 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
isEventLoadingLoading = false;
notifyListeners();
Utils.handleException(ex, null, null);
ticketBookingResponse = TicketBookingResult(false, null);
ticketBookingResponse = TicketBookingResult(false, null); // Ensure a return value in case of an exception
}
}
void fetchTicketBalance() async {
try {
GenericResponseModel? ticketResponse = await DashboardApiClient().getTicketBalance();
logger.d("Ticket Balance Response: ${ticketResponse?.toJson()}");
print(" ========= Ticket Balance Response ======= ");
} catch (ex) {
logger.wtf(ex);
isEventLoadingLoading = false;
@ -379,7 +340,8 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
Future<SSOAuthModel?> fetchSSOAuthRedirection({String? clientID}) async {
try {
SSOAuthModel? ssoResponse = await DashboardApiClient().getBookingSSOAuthRedirection(clientID: clientID!);
if (ssoResponse!.status == "Success") {
logger.d("SSO Response: ${ssoResponse!.toJson()}");
if (ssoResponse.status == "Success") {
return ssoResponse;
} else {
Utils.showToast(ssoResponse.message!.first ?? "Failed to fetch SSO Auth Token");
@ -394,28 +356,17 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
}
Future<dynamic> fetchURLRedirection({required String token, required String refreshToken}) async {
Future<dynamic> fetchURLRedirection({required String token}) async {
try {
dynamic res = await DashboardApiClient().getBookingSSOFinalRedirection(token: token, refreshToken: refreshToken);
dynamic res = await DashboardApiClient().getBookingSSOFinalRedirection(token: token);
logger.d("SSO URL: $res");
return res;
} catch (ex) {
logger.wtf(ex);
isEventLoadingLoading = false;
notifyListeners();
Utils.handleException(ex, null, null);
return null;
}
}
Future<GetEitAttachmentTicket?> getTicketEitPDFDownload({String? peiExtraInfoId}) async {
try {
GetEitAttachmentTicket? res = await DashboardApiClient().getTicketEitPDFDownload(peiExtraInfoId: peiExtraInfoId!);
if (res?.getEitAttachmentList != null && res?.getEitAttachmentList!.length! != 0 && res!.getEitAttachmentList!.first.fileData != null) {
return res;
} else {
return null;
}
// if (ssoResponse.status == "Success") {
// return ssoResponse;
// } else {
// Utils.showToast(ssoResponse.message!.first ?? "Failed to fetch SSO Auth Token");
// return null;
// }
} catch (ex) {
logger.wtf(ex);
isEventLoadingLoading = false;
@ -458,35 +409,6 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
return res;
}
Future<String?> getENSATItgToken() async {
String? res = await DashboardApiClient().getENSATItgToken();
return res;
}
//Courses API's & Methods
Future<void> fetchCourses(BuildContext context) async {
try {
isCoursesLoading = true;
notifyListeners();
coursesResponse = await DashboardApiClient().fetchAllCoursesStatus();
if (coursesResponse != null && coursesResponse!.success == true) {
coursesList = coursesResponse!.coursesStatus ?? [];
} else {
coursesList = [];
}
isCoursesLoading = false;
notifyListeners();
} catch (ex) {
isCoursesLoading = false;
coursesList = [];
notifyListeners();
Utils.handleException(ex, context, null);
}
}
void notify() {
notifyListeners();
}

@ -1,262 +0,0 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/api/etqan_ovr_api_client.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_employee_incident_report.dart';
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_requests_model.dart';
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_getprojects_model.dart';
import 'package:speech_to_text/speech_recognition_result.dart';
import 'package:speech_to_text/speech_to_text.dart';
class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
// Home screen state
List<EtqanGetProjectsResponse>? getEtqanProjectsList;
List<EtqanGetEmployeeOvrRequestsResponse>? getEtqanEmployeeRequestsList;
List<EtqanGetIncidentRequestResponse>? getEtqanEmployeeIncidnetRequest;
bool isLoading = false;
String? _ticketId;
String? _currentStatus;
String? _currentStatusAr;
bool? _currentIsAnonymous;
// Speech to text state
final SpeechToText _speechToText = SpeechToText();
bool _speechEnabled = false;
bool _isListening = false;
String _currentLocaleId = '';
TextEditingController? _currentController;
String _baseText = ''; // Text that was there when we started listening
String _currentSessionText = ''; // Text captured in current listening session
// Getters for speech state
bool get speechEnabled => _speechEnabled;
bool get isListening => _isListening;
// Getter for ticketId
String get getTicketId => _ticketId ?? '';
String get getCurrentStatus => _currentStatus ?? '';
String get getCurrentStatusAr => _currentStatusAr ?? '';
bool get getCurrentIsAnonymous => _currentIsAnonymous ?? false;
// Setter for ticketId with status and isAnonymous
void setTicketId(String ticketId, {String? status, String? statusAr, bool? isAnonymous}) {
_ticketId = ticketId;
_currentStatus = status;
_currentStatusAr = statusAr;
_currentIsAnonymous = isAnonymous;
notifyListeners();
}
// Create request screen state
EtqanGetProjectsResponse? selectedProject;
String? selectedEmployeeNumber;
File? attachment;
String? attachmentBase64;
String description = '';
bool isAnonymous = false;
/// Initialize speech recognition - call this once when screen loads
Future<void> initSpeech() async {
_speechEnabled = await _speechToText.initialize(
onStatus: (String status) {
print('Speech status: $status, isListening: $_isListening');
// If user wants to keep listening but speech stopped, restart it
if ((status == 'done' || status == 'notListening') && _isListening && _currentController != null) {
// Save the finalized text before restarting
if (_currentSessionText.isNotEmpty) {
_baseText = _currentController!.text.trim();
_currentSessionText = '';
}
// Automatically restart listening after a brief pause
Future.delayed(const Duration(milliseconds: 300), () {
if (_isListening && _currentController != null) {
_startListeningInternal(_currentLocaleId, _currentController!);
}
});
}
},
);
notifyListeners();
}
/// Internal method to start listening
Future<void> _startListeningInternal(String localeId, TextEditingController controller) async {
if (!_speechEnabled) return;
await _speechToText.listen(
onResult: (SpeechRecognitionResult result) {
_currentSessionText = result.recognizedWords;
// Combine base text with current session text
String combinedText = _baseText.isEmpty
? _currentSessionText
: _baseText + (_baseText.endsWith(' ') ? '' : ' ') + _currentSessionText;
description = combinedText;
controller.text = combinedText;
controller.selection = TextSelection.fromPosition(TextPosition(offset: controller.text.length));
notifyListeners();
},
localeId: localeId,
listenFor: const Duration(seconds: 60),
pauseFor: const Duration(seconds: 10),
listenOptions: SpeechListenOptions(
partialResults: true,
listenMode: ListenMode.confirmation,
cancelOnError: false,
),
);
}
/// Start listening for speech input
Future<void> startListening(String localeId, TextEditingController controller) async {
_isListening = true;
_currentLocaleId = localeId;
_currentController = controller;
_baseText = controller.text.trim(); // Save existing text
_currentSessionText = '';
notifyListeners();
await _startListeningInternal(localeId, controller);
}
/// Stop listening for speech input
Future<void> stopListening() async {
_isListening = false;
_currentLocaleId = '';
_currentController = null;
_baseText = '';
_currentSessionText = '';
await _speechToText.stop();
notifyListeners();
}
/// Toggle listening state
Future<void> toggleListening(String localeId, TextEditingController controller) async {
if (_isListening) {
await stopListening();
} else {
await startListening(localeId, controller);
}
}
// Fetch Etqan Projects
Future<void> fetchEtqanProjects(BuildContext context) async {
isLoading = true;
notifyListeners();
Utils.showLoading(context);
getEtqanProjectsList?.clear();
getEtqanProjectsList = await EtqanApiClient().getEtqanProjects();
Utils.hideLoading(context);
isLoading = false;
notifyListeners();
}
// Fetch Etqan Employee Requests
Future<void> fetchEtqanEmployeeRequests(BuildContext context) async {
isLoading = true;
notifyListeners();
Utils.showLoading(context);
getEtqanEmployeeRequestsList?.clear();
getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests();
Utils.hideLoading(context);
isLoading = false;
notifyListeners();
}
// Fetch Etqan Employee Incident Report
Future<void> fetchEtqanEmployeeIncidentRequests(BuildContext context, String ticketNo) async {
isLoading = true;
notifyListeners();
Utils.showLoading(context);
getEtqanEmployeeIncidnetRequest?.clear();
getEtqanEmployeeIncidnetRequest = await EtqanApiClient().getEtqanIncidentRequests(ticketNo);
Utils.hideLoading(context);
isLoading = false;
notifyListeners();
}
// Set selected project
void setSelectedProject(EtqanGetProjectsResponse? project) {
selectedProject = project;
notifyListeners();
}
// Set selected employee number
void setSelectedEmployeeNumber(String? employeeNumber) {
selectedEmployeeNumber = employeeNumber;
notifyListeners();
}
// Set attachment with base64
void setAttachment(File file, String base64) {
attachment = file;
attachmentBase64 = base64;
notifyListeners();
}
// Remove attachment
void removeAttachment() {
attachment = null;
attachmentBase64 = null;
notifyListeners();
}
// Set description
void setDescription(String value) {
description = value;
notifyListeners();
}
// Validate description
bool isDescriptionValid() {
return description.trim().isNotEmpty;
}
// Set isAnonymous
void setIsAnonymous(bool value) {
isAnonymous = value;
notifyListeners();
}
// Clear create request form
void clearCreateRequestForm() {
selectedProject = null;
selectedEmployeeNumber = null;
attachment = null;
attachmentBase64 = null;
description = '';
isAnonymous = false;
notifyListeners();
}
// Submit request
Future<Map<String, dynamic>?> submitRequest(BuildContext context) async {
try {
Utils.showLoading(context);
Map<String, dynamic>? response = await EtqanApiClient().createRequest(description, selectedProject?.projectId, attachmentBase64, isAnonymous);
Utils.hideLoading(context);
if (response != null) {
getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests();
notifyListeners();
return response;
}
return null;
} catch (ex) {
Utils.hideLoading(context);
Utils.handleException(ex, context, null);
return null;
}
}
@override
void dispose() {
if (_isListening) {
_speechToText.stop();
}
super.dispose();
}
}

@ -1,3 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mohem_flutter_app/classes/colors.dart';

@ -57,15 +57,6 @@ class _MonthlyAttendanceScreenState extends State<MonthlyAttendanceScreen> {
Utils.showLoading(context);
getTimeCardSummaryList = await MonthlyAttendanceApiClient().getTimeCardSummary(searchMonth, searchYear);
getDayHoursTypeDetailsList = await MonthlyAttendanceApiClient().getDayHoursTypeDetails(searchMonth, searchYear);
// Debug logging to check the data
print('Total days received: ${getDayHoursTypeDetailsList.length}');
for (var day in getDayHoursTypeDetailsList) {
if (day.lEAVETYPECODE != null && day.lEAVETYPECODE!.isNotEmpty) {
print('Date: ${day.sCHEDULEDATE}, LEAVE_TYPE_CODE: ${day.lEAVETYPECODE}');
}
}
Utils.hideLoading(context);
_calendarController.displayDate = formattedDate;
setState(() {});
@ -310,49 +301,19 @@ class _MonthlyAttendanceScreenState extends State<MonthlyAttendanceScreen> {
monthCellBuilder: (build, details) {
if (details.date.month == formattedDate.month && details.date.year == formattedDate.year) {
int val = details.date.day;
int index = val-1;
//check day is off
// if (getDayHoursTypeDetailsList.isNotEmpty) {
// bool isDayIsOff = getDayHoursTypeDetailsList[val - 1].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[val - 1].dAYTYPE == 'OFF';
// bool isDayIsPresent = getDayHoursTypeDetailsList[val - 1].aTTENDEDFLAG == 'Y';
// bool isDayIsAbsent = getDayHoursTypeDetailsList[val - 1].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[val - 1].aBSENTFLAG == 'Y';
List<GetDayHoursTypeDetailsList> getDayHours =
getDayHoursTypeDetailsList.where((GetDayHoursTypeDetailsList element) {
DateTime scheduleDate = DateFormat("MM/dd/yyyy", "en_US").parse(element.sCHEDULEDATE!);
return scheduleDate.day == details.date.day &&
scheduleDate.month == details.date.month &&
scheduleDate.year == details.date.year;
}).toList();
getDayHoursTypeDetailsList.where((GetDayHoursTypeDetailsList element) => DateFormat("MM/dd/yyyy", "en_US").parse(element.sCHEDULEDATE!).day == details.date.day).toList();
if (getDayHours.isNotEmpty) {
GetDayHoursTypeDetailsList dayData = getDayHours.first;
// Debug logging
print('Day: ${details.date.day}, LEAVE_TYPE_CODE: ${dayData.lEAVETYPECODE}, ATTENDED_FLAG: ${dayData.aTTENDEDFLAG}, DAY_TYPE: ${dayData.dAYTYPE}');
bool isDayIsOff = dayData.aTTENDEDFLAG == 'N' && dayData.dAYTYPE == 'OFF';
bool isDayIsPresent = dayData.aTTENDEDFLAG == 'Y';
bool isDayIsAbsent = dayData.aTTENDEDFLAG == 'N' && dayData.aBSENTFLAG == 'Y';
// Get leave type code and corresponding color
String? leaveTypeCode = dayData.lEAVETYPECODE;
Color? leaveTypeColor;
bool useGradient = false;
// Determine color based on leave type code
if (leaveTypeCode != null && leaveTypeCode.isNotEmpty && leaveTypeCode.trim().isNotEmpty) {
leaveTypeColor = leaveTypeColors[leaveTypeCode];
print('Matched color for $leaveTypeCode: $leaveTypeColor');
}
// If no leave type color found, use default colors based on attendance status
if (leaveTypeColor == null) {
if (isDayIsPresent) {
leaveTypeColor = leaveTypeColors['Present'];
useGradient = true; // Use gradient for present days without leave type
} else if (isDayIsAbsent) {
leaveTypeColor = leaveTypeColors['Absent'];
}
}
bool isDayIsOff = getDayHoursTypeDetailsList[index].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[index].dAYTYPE == 'OFF';
bool isDayIsPresent = getDayHoursTypeDetailsList[index].aTTENDEDFLAG == 'Y';
bool isDayIsAbsent = getDayHoursTypeDetailsList[index].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[index].aBSENTFLAG == 'Y';
if (isDayIsOff) {
return Container(
@ -370,13 +331,12 @@ class _MonthlyAttendanceScreenState extends State<MonthlyAttendanceScreen> {
return Container(
margin: const EdgeInsets.all(4),
decoration: BoxDecoration(
gradient: useGradient && leaveTypeColor != null ? LinearGradient(
gradient: const LinearGradient(
transform: GradientRotation(.46),
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [MyColors.gradiantEndColor, MyColors.gradiantStartColor],
) : null,
color: !useGradient && leaveTypeColor != null ? leaveTypeColor : null,
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
@ -398,29 +358,7 @@ class _MonthlyAttendanceScreenState extends State<MonthlyAttendanceScreen> {
return Container(
margin: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: leaveTypeColor ?? MyColors.backgroundBlackColor,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
offset: const Offset(0, 2),
blurRadius: 26,
color: MyColors.blackColor.withOpacity(0.100),
),
],
),
alignment: Alignment.center,
child: Text(
"$val",
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500, color: MyColors.white),
),
);
}
// Handle days with leave type codes (HOLIDAY, WFH, etc.) that are not present/absent/off
else if (leaveTypeColor != null) {
return Container(
margin: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: leaveTypeColor,
color: MyColors.backgroundBlackColor,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
@ -469,26 +407,12 @@ class _MonthlyAttendanceScreenState extends State<MonthlyAttendanceScreen> {
}
void calendarTapped(CalendarTapDetails details) async {
if (details.date == null) return;
// Filter to find the matching day data with full date comparison
List<GetDayHoursTypeDetailsList> getDayHours =
getDayHoursTypeDetailsList.where((GetDayHoursTypeDetailsList element) {
DateTime scheduleDate = DateFormat("MM/dd/yyyy", "en_US").parse(element.sCHEDULEDATE!);
return scheduleDate.day == details.date!.day &&
scheduleDate.month == details.date!.month &&
scheduleDate.year == details.date!.year;
}).toList();
if (getDayHours.isEmpty) return;
GetDayHoursTypeDetailsList dayData = getDayHours.first;
// Debug logging
print('Tapped Day: ${details.date!.day}, LEAVE_TYPE_CODE: ${dayData.lEAVETYPECODE}');
pRTPID = dayData.rTPID;
await getScheduleShiftsDetails(details.date!.day, pRTPID);
dynamic index = details.date?.day;
if (index != null) {
index = index - 1;
}
pRTPID = getDayHoursTypeDetailsList[index].rTPID;
await getScheduleShiftsDetails(index, pRTPID);
dynamic value = getScheduleShiftsDetailsList!.pERCENTAGE;
dynamic percentage;
if (value!.indexOf('%') == 3) {
@ -501,7 +425,8 @@ class _MonthlyAttendanceScreenState extends State<MonthlyAttendanceScreen> {
percentage = 0;
}
if (details.date!.month == formattedDate.month && details.date!.year == formattedDate.year) {
dayData.aTTENDEDFLAG == 'Y'
int val = details.date!.day;
getDayHoursTypeDetailsList?[val - 1].aTTENDEDFLAG == 'Y'
? showModalBottomSheet(
context: context,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)),
@ -646,7 +571,7 @@ class _MonthlyAttendanceScreenState extends State<MonthlyAttendanceScreen> {
LocaleKeys.lateIn.tr().toText11(
color: MyColors.grey67Color,
),
"${dayData.lATEINHRS}".toText22(color: MyColors.redColor, isBold: true),
"${getDayHoursTypeDetailsList[index].lATEINHRS}".toText22(color: MyColors.redColor, isBold: true),
],
).expanded,
Column(
@ -655,7 +580,7 @@ class _MonthlyAttendanceScreenState extends State<MonthlyAttendanceScreen> {
LocaleKeys.excess.tr().toText11(
color: MyColors.grey67Color,
),
"${dayData.eXCESSHRS}".toText22(color: MyColors.backgroundBlackColor, isBold: true),
"${getDayHoursTypeDetailsList[index].eXCESSHRS}".toText22(color: MyColors.backgroundBlackColor, isBold: true),
],
).expanded,
],
@ -675,7 +600,7 @@ class _MonthlyAttendanceScreenState extends State<MonthlyAttendanceScreen> {
LocaleKeys.shortage.tr().toText11(
color: MyColors.grey67Color,
),
"${dayData.sHORTAGEHRS}".toText22(color: MyColors.backgroundBlackColor, isBold: true),
"${getDayHoursTypeDetailsList[index].sHORTAGEHRS}".toText22(color: MyColors.backgroundBlackColor, isBold: true),
],
).expanded,
Column(
@ -684,7 +609,7 @@ class _MonthlyAttendanceScreenState extends State<MonthlyAttendanceScreen> {
LocaleKeys.earlyOut.tr().toText11(
color: MyColors.grey67Color,
),
"${dayData.eARLYOUTHRS}".toText22(color: Colors.black, isBold: true),
"${getDayHoursTypeDetailsList[index].eARLYOUTHRS}".toText22(color: Colors.black, isBold: true),
],
).expanded,
],
@ -826,24 +751,3 @@ class Meeting {
Color background;
bool isAllDay;
}
const Map<String, Color> leaveTypeColors = {
'Present': Color(0xFF00ad62),
'Absent': Color(0xFFcb3232),
'HOLIDAY': Color(0xFFF26B0F),
'BUS_TRIP': Color(0xFF001A6E),
'WFH': Color(0xFF5DB996),
'ANNUAL': Color(0xFFf39c12),
'EMERGENCY': Color(0xFFFF2929),
'EXAM': Color(0xFFCB6040),
'HAJJ': Color(0xFF798645),
'HALF': Color(0xFFE8B86D),
'LWOP': Color(0xFFCD5C08),
'LWOP_MATUNPAID': Color(0xFFE73879),
'LWOP_UNAUUNPAID': Color(0xFFA02334),
'MATPAID': Color(0xFFE73879),
'MRGE': Color(0xFF0D7C66),
'PAT': Color(0xFFC96868),
'PROF': Color(0xFF6256CA),
'SICK': Color(0xFFA5B68D),
};

@ -0,0 +1,499 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_svg/svg.dart';
import 'package:mohem_flutter_app/api/dashboard_api_client.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/utils.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/generic_response_model.dart';
import 'package:speech_to_text/speech_recognition_result.dart';
import 'package:speech_to_text/speech_to_text.dart';
class MessagesHolder{
static List<ChatMessage> messages = [];
}
enum ChatMode { home, chatActive }
class ChatMessage {
final String text;
final bool isUser;
final DateTime timestamp;
ChatMessage({required this.text, required this.isUser, required this.timestamp});
}
class ChatBotScreen extends StatefulWidget {
const ChatBotScreen({Key? key}) : super(key: key);
@override
State<ChatBotScreen> createState() => _ChatBotScreenState();
}
class _ChatBotScreenState extends State<ChatBotScreen> {
ChatMode _currentMode = ChatMode.home;
final List<ChatMessage> _messages = <ChatMessage>[];
final TextEditingController _messageController = TextEditingController();
final ScrollController _scrollController = ScrollController();
AtlasInitiateChatResponse? atlasChatTokenResponse;
bool _isThinking = false;
// Speech to text
final SpeechToText _speechToText = SpeechToText();
bool _speechEnabled = false;
@override
void initState() {
// TODO: implement initState
super.initState();
getChatSession();
_initSpeech();
}
/// Initialize speech to text
void _initSpeech() async {
_speechEnabled = await _speechToText.initialize();
if(MessagesHolder.messages.isNotEmpty){
setState(() {
_messages.addAll(MessagesHolder.messages);
_currentMode = ChatMode.chatActive;
});
}
setState(() {});
}
/// Start listening for speech
void _startListening() async {
await _speechToText.listen(
onResult: _onSpeechResult,
localeId: AppState().isArabic(context) ? 'ar_SA' : 'en_US',
);
setState(() {});
}
/// Stop listening for speech
void _stopListening() async {
await _speechToText.stop();
setState(() {});
}
/// Callback when speech is recognized
void _onSpeechResult(SpeechRecognitionResult result) {
setState(() {
_messageController.text = result.recognizedWords;
// Move cursor to end of text
_messageController.selection = TextSelection.fromPosition(TextPosition(offset: _messageController.text.length));
});
}
void getChatSession() async {
try {
GenericResponseModel? res = await DashboardApiClient().getChatBotSession();
if (res != null) {
setState(() {
atlasChatTokenResponse = res.atlasInitiateChatResponse;
});
}
} catch (ex) {
Utils.handleException(ex, context, null);
}
}
@override
void dispose() {
atlasChatTokenResponse = null;
_messageController.dispose();
_scrollController.dispose();
super.dispose();
}
void _sendMessage({String? suggestionText}) async {
String text = suggestionText ?? _messageController.text.trim();
if (text.isEmpty || atlasChatTokenResponse == null) return;
try {
setState(() {
_messages.add(ChatMessage(text: text, isUser: true, timestamp: DateTime.now()));
_currentMode = ChatMode.chatActive;
_messageController.clear();
_isThinking = true;
});
_scrollToBottom();
GenericResponseModel? res = await DashboardApiClient().sendChatBotMessage(atlasChatTokenResponse: atlasChatTokenResponse!, atlasChatText: text);
if (res != null) {
res.atlasContinueChatResponseDetails;
if (mounted) {
setState(() {
_isThinking = false;
_messages.add(
ChatMessage(
text: res.atlasContinueChatResponseDetails!.aiResponse!.structuredData!.answer ?? "",
isUser: false,
timestamp: res.atlasContinueChatResponseDetails!.aiResponse!.createdAt ?? DateTime.now(),
),
);
});
}
_scrollToBottom();
}
} catch (ex) {
setState(() {
_isThinking = false;
});
Utils.handleException(ex, context, null);
}
_scrollToBottom();
}
void _scrollToBottom() {
Future<void>.delayed(const Duration(milliseconds: 100), () {
if (_scrollController.hasClients) {
_scrollController.animateTo(_scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 300), curve: Curves.easeOut);
}
});
}
void _sendSuggestion(String text) {
if (atlasChatTokenResponse == null) {
Utils.showToast(LocaleKeys.initializingChatPleaseWait.tr());
return;
}
_sendMessage(suggestionText: text);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: MyColors.backgroundColor,
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(LocaleKeys.chatBot.tr(), style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w700, color: Color(0xFF2B353E))),
backgroundColor: MyColors.backgroundColor,
centerTitle: false,
actions: [
IconButton(
icon: const Icon(Icons.minimize, color: Color(0xFF2B353E)),
onPressed: () {
MessagesHolder.messages = _messages;
Navigator.of(context).pop();
},
),
IconButton(
icon: const Icon(Icons.close, color: Color(0xFF2B353E)),
onPressed: () {
MessagesHolder.messages.clear();
Navigator.of(context).pop();
},
),
],
),
body: Column(children: <Widget>[Expanded(child: _currentMode == ChatMode.home ? _buildHomeMode() : _buildChatMode()), _buildBottomInputBar()]),
);
}
Widget _buildHomeMode() {
return Center(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(LocaleKeys.helloAskMe.tr(), textAlign: TextAlign.center, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w700, color: Color(0xFF2E303A))),
Text(LocaleKeys.anything.tr(), textAlign: TextAlign.center, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w700, color: Color(0xFF2E303A))),
const SizedBox(height: 50),
const Wrap(
alignment: WrapAlignment.center,
spacing: 8,
runSpacing: 12,
children: <Widget>[
// _buildSuggestionChip("Nostalgia Perfume", () => _sendSuggestion("Nostalgia Perfume")),
// _buildSuggestionChip("Al Nafoura", () => _sendSuggestion("Al Nafoura")),
// _buildSuggestionChip("Al Nafoura", () => _sendSuggestion("Al Nafoura")),
// _buildSuggestionChip("Al Jadi", () => _sendSuggestion("Al Jadi")),
],
),
],
),
),
);
}
Widget _buildChatMode() {
return ListView.separated(
controller: _scrollController,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
itemCount: _messages.length + (_isThinking ? 1 : 0),
itemBuilder: (BuildContext context, int index) {
if (index == _messages.length && _isThinking) {
return _buildThinkingIndicator();
}
ChatMessage message = _messages[index];
return _buildMessageBubble(message);
},
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 15);
},
);
}
Widget _buildThinkingIndicator() {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
gradient: const LinearGradient(colors: <Color>[Color(0xFF32D892), Color(0xFF259CB8)], begin: Alignment.centerLeft, end: Alignment.centerRight),
borderRadius: BorderRadius.circular(15),
boxShadow: <BoxShadow>[BoxShadow(color: const Color(0xFF000000).withValues(alpha: 0.0784), offset: const Offset(0, 0), blurRadius: 13, spreadRadius: 0)],
),
child: const ThinkingDotsAnimation(),
),
const Spacer(flex: 2),
],
);
}
Widget _buildMessageBubble(ChatMessage message) {
bool isUser = message.isUser;
Widget bubbleContent = Row(
mainAxisAlignment: isUser ? MainAxisAlignment.end : MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
if (isUser) const Spacer(flex: 2),
Flexible(
flex: 7,
child: Column(
crossAxisAlignment: isUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: isUser ? Colors.white : null,
gradient: isUser ? null : const LinearGradient(colors: <Color>[Color(0xFF32D892), Color(0xFF259CB8)], begin: Alignment.centerLeft, end: Alignment.centerRight),
borderRadius: BorderRadius.circular(15),
boxShadow: isUser ? null : <BoxShadow>[BoxShadow(color: const Color(0xFF000000).withValues(alpha: 0.0784), offset: const Offset(0, 0), blurRadius: 13, spreadRadius: 0)],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(message.text, softWrap: true, style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: isUser ? const Color(0xFF2B353E) : MyColors.white)),
Align(
alignment: Alignment.centerRight,
child: Text(
_formatTimestamp(message.timestamp),
style: TextStyle(fontSize: 11, fontWeight: FontWeight.w500, letterSpacing: -0.4, color: isUser ? const Color(0xFF2B353E) : MyColors.whiteColor),
),
),
],
),
),
],
),
),
if (!isUser) const Spacer(flex: 2),
],
);
if (!isUser) {
return AnimatedMessageBubble(child: bubbleContent);
}
return bubbleContent;
}
String _formatTimestamp(DateTime timestamp) {
int hourInt = timestamp.hour;
int minute = timestamp.minute;
String period = hourInt >= 12 ? LocaleKeys.pm.tr() : LocaleKeys.am.tr();
int formattedHour = hourInt % 12 == 0 ? 12 : hourInt % 12;
String hour = formattedHour.toString().padLeft(2, '0');
String min = minute.toString().padLeft(2, '0');
String month = timestamp.month.toString().padLeft(2, '0');
String day = timestamp.day.toString().padLeft(2, '0');
int year = timestamp.year;
return "$month/$day/$year $hour:$min $period";
}
Widget _buildSuggestionChip(String label, VoidCallback onTap) {
return Material(
color: Colors.white,
elevation: 3,
shadowColor: const Color(0xFF000015).withValues(alpha: 0.0784),
borderRadius: BorderRadius.circular(10),
child: InkWell(
borderRadius: BorderRadius.circular(10),
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Text(label, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: MyColors.darkTextColor, letterSpacing: 0.2)),
),
),
);
}
Widget _buildBottomInputBar() {
return Container(
decoration: const BoxDecoration(color: MyColors.backgroundColor),
child: SafeArea(
top: false,
child: Row(
children: <Widget>[
// Plus button
// IconButton(padding: EdgeInsets.zero, icon: const Icon(Icons.add, color: MyColors.darkTextColor, size: 34), onPressed: () {}),
// const SizedBox(width: 12),
// Text field
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
height: 48,
decoration: BoxDecoration(color: Colors.transparent, borderRadius: BorderRadius.circular(10), border: BoxBorder.all(width: 1, color: const Color(0xFFE5E5E5))),
child: Row(
children: <Widget>[
Expanded(
child: TextField(
controller: _messageController,
onSubmitted: _isThinking ? null : (_) => _sendMessage(),
enabled: !_isThinking,
maxLines: 1,
textAlignVertical: TextAlignVertical.center,
decoration: InputDecoration(
hintText: _isThinking ? LocaleKeys.waitingForResponse.tr() : LocaleKeys.typeAMessage.tr(),
hintStyle: const TextStyle(color: MyColors.hintTextColor, fontSize: 14, fontWeight: FontWeight.normal),
border: InputBorder.none,
isDense: true,
contentPadding: const EdgeInsets.symmetric(vertical: 10),
),
),
),
GestureDetector(
onTap: _isThinking ? null : (_speechEnabled ? (_speechToText.isNotListening ? _startListening : _stopListening) : null),
child: SvgPicture.asset(
"assets/icons/microphone.svg",
colorFilter: ColorFilter.mode(_isThinking ? MyColors.hintTextColor : (_speechToText.isListening ? Colors.red : MyColors.darkTextColor), BlendMode.srcIn),
),
),
],
),
),
),
const SizedBox(width: 6),
// Send button
GestureDetector(
onTap: _isThinking ? null : _sendMessage,
child: Container(
width: 48,
height: 48,
decoration: BoxDecoration(color: _isThinking ? MyColors.hintTextColor.withValues(alpha: 0.3) : MyColors.white, shape: BoxShape.circle),
child: Center(
child: SvgPicture.asset(
"assets/icons/send.svg",
matchTextDirection: true,
colorFilter: _isThinking ? const ColorFilter.mode(MyColors.hintTextColor, BlendMode.srcIn) : null,
),
),
),
),
],
),
),
).paddingOnly(left: 16, right: 16, bottom: 16);
}
}
class ThinkingDotsAnimation extends StatefulWidget {
const ThinkingDotsAnimation({Key? key}) : super(key: key);
@override
State<ThinkingDotsAnimation> createState() => _ThinkingDotsAnimationState();
}
class _ThinkingDotsAnimationState extends State<ThinkingDotsAnimation> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 1200))..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(LocaleKeys.thinking.tr(), style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Colors.white)),
const SizedBox(width: 2),
AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget? child) {
int dotCount = (_controller.value * 4).floor() % 4;
String dots = '.' * dotCount;
return SizedBox(width: 18, child: Text(dots, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Colors.white)));
},
),
],
);
}
}
class AnimatedMessageBubble extends StatefulWidget {
final Widget child;
const AnimatedMessageBubble({Key? key, required this.child}) : super(key: key);
@override
State<AnimatedMessageBubble> createState() => _AnimatedMessageBubbleState();
}
class _AnimatedMessageBubbleState extends State<AnimatedMessageBubble> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _fadeAnimation;
late Animation<Offset> _slideAnimation;
late Animation<double> _scaleAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 400));
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut));
_slideAnimation = Tween<Offset>(begin: const Offset(-0.3, 0.0), end: Offset.zero).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic));
_scaleAnimation = Tween<double>(begin: 0.8, end: 1.0).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOutBack));
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SlideTransition(
position: _slideAnimation,
child: FadeTransition(opacity: _fadeAnimation, child: ScaleTransition(scale: _scaleAnimation, alignment: Alignment.centerLeft, child: widget.child)),
);
}
}

@ -1,360 +0,0 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.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/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_getprojects_model.dart';
import 'package:mohem_flutter_app/provider/etqan_ovr_provider.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:mohem_flutter_app/widgets/image_picker.dart';
import 'package:provider/provider.dart';
class EtqanOvrCreateRequest extends StatefulWidget {
const EtqanOvrCreateRequest({Key? key}) : super(key: key);
@override
_EtqanOvrCreateRequestState createState() => _EtqanOvrCreateRequestState();
}
class _EtqanOvrCreateRequestState extends State<EtqanOvrCreateRequest> {
final TextEditingController _descriptionController = TextEditingController();
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
var provider = context.read<EtqanOvrProviderModel>();
provider.initSpeech();
provider.fetchEtqanProjects(context);
provider.clearCreateRequestForm();
});
}
@override
void dispose() {
// Stop listening if active
context.read<EtqanOvrProviderModel>().stopListening();
_descriptionController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBarWidget(context, title: LocaleKeys.reportIncident.tr(), showHomeButton: false),
body: Consumer<EtqanOvrProviderModel>(
builder: (BuildContext context, EtqanOvrProviderModel provider, Widget? child) {
return Column(
children: <Widget>[
(ListView(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.all(21),
children: <Widget>[
_buildDropdownField(
label: LocaleKeys.project.tr(),
hint: LocaleKeys.selectProject.tr(),
value: _getProjectDisplayName(provider.selectedProject),
onTap: () => _showProjectSelectionSheet(context, provider),
isMandatory: true,
),
16.height,
// _buildDropdownField(
// label: 'Employee Number',
// hint: 'Write a message',
// value: provider.selectedEmployeeNumber,
// onTap: () {
// // TODO: Show employee selection dialog
// },
// ),
// 16.height,
_buildAttachmentSection(provider),
16.height,
_buildDescriptionField(provider),
8.height,
_buildAnonymousCheckbox(provider),
24.height,
],
)).expanded,
DefaultButton(LocaleKeys.submit.tr(), () async {
provider.setDescription(_descriptionController.text);
if (provider.selectedProject == null) {
Utils.showErrorDialog(
context: context,
message: LocaleKeys.pleaseSelectProject.tr(),
onOkTapped: () {
Navigator.of(context).pop();
},
);
return;
}
if (!provider.isDescriptionValid()) {
Utils.showErrorDialog(
context: context,
message: LocaleKeys.pleaseEnterDescription.tr(),
onOkTapped: () {
Navigator.of(context).pop();
},
);
return;
}
Map<String, dynamic>? ticketInfo = await provider.submitRequest(context);
if (ticketInfo != null) {
String ticketNumber = ticketInfo['ticketNumber'] ?? '';
String ticketId = ticketInfo['id'] ?? '';
// String successMessage = '${LocaleKeys.requestCreatedSuccessfully.tr()}\n\n${LocaleKeys.ticketNumber.tr()}: $ticketNumber\n${LocaleKeys.ticketId.tr()}: $ticketId';
String successMessage = '${LocaleKeys.requestCreatedSuccessfully.tr()}\n\n${LocaleKeys.ticketNumber.tr()}: $ticketNumber';
Utils.showErrorDialog(
context: context,
message: successMessage,
onOkTapped: () {
Navigator.pop(context);
Navigator.pop(context);
// Navigator.popAndPushNamed(context, AppRoutes.etqanOvr);
},
onCloseTap: () {
Navigator.pop(context);
Navigator.pop(context);
// Navigator.popAndPushNamed(context, AppRoutes.etqanOvr);
},
);
}
}).insideContainer,
],
);
},
),
);
}
String? _getProjectDisplayName(EtqanGetProjectsResponse? project) {
if (project == null) return null;
return AppState().isArabic(context) ? project.projectAr : project.projectEn;
}
void _showProjectSelectionSheet(BuildContext context, EtqanOvrProviderModel provider) {
if (provider.getEtqanProjectsList == null || provider.getEtqanProjectsList!.isEmpty) {
return;
}
showMyBottomSheet(
context,
callBackFunc: () {},
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(LocaleKeys.selectProject.tr(), style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Color(0xff2B353E))),
),
const Divider(),
ListView.separated(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
itemCount: provider.getEtqanProjectsList!.length,
separatorBuilder: (BuildContext context, int index) => const Divider(height: 1),
itemBuilder: (BuildContext context, int index) {
EtqanGetProjectsResponse project = provider.getEtqanProjectsList![index];
bool isSelected = provider.selectedProject?.projectId == project.projectId;
return ListTile(
title: Text(
AppState().isArabic(context) ? (project.projectAr ?? '') : (project.projectEn ?? ''),
style: TextStyle(fontSize: 14, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, color: isSelected ? MyColors.gradiantEndColor : const Color(0xff2B353E)),
),
trailing: isSelected ? const Icon(Icons.check, color: MyColors.gradiantEndColor) : null,
onTap: () {
provider.setSelectedProject(project);
Navigator.pop(context);
},
);
},
),
16.height,
],
),
);
}
Widget _buildDropdownField({required String label, required String hint, String? value, required VoidCallback onTap, bool isMandatory = false}) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(15),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all(color: const Color(0xffefefef), width: 1)),
child: Row(
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
Text(label, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.44)),
if (isMandatory) const Text(' *', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Colors.red, letterSpacing: -0.44)),
],
),
const SizedBox(height: 4),
Text(value ?? hint, style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: value != null ? const Color(0xff2B353E) : const Color(0xff575757), letterSpacing: -0.56)),
],
),
),
const Icon(Icons.keyboard_arrow_down, color: Color(0xff575757)),
],
),
),
);
}
Widget _buildAttachmentSection(EtqanOvrProviderModel provider) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all(color: const Color(0xffefefef), width: 1)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(LocaleKeys.supportingDocument.tr(), style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xff2B353E))),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(LocaleKeys.attachments.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xff2B353E))),
if (provider.attachment == null)
InkWell(
onTap: () {
ImageOptions.showImageOptionsNew(context, true, (String base64, File file) {
provider.setAttachment(file, base64);
Navigator.of(context).pop();
});
},
borderRadius: BorderRadius.circular(8),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
decoration: BoxDecoration(color: MyColors.gradiantEndColor, borderRadius: BorderRadius.circular(8)),
child: Text(LocaleKeys.add.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Colors.white)),
),
),
],
),
if (provider.attachment != null) ...<Widget>[
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: <Widget>[
const Icon(Icons.attach_file, size: 18, color: Color(0xff575757)),
const SizedBox(width: 8),
Expanded(child: Text(provider.attachment!.path.split('/').last, style: const TextStyle(fontSize: 12, color: Color(0xff575757)), overflow: TextOverflow.ellipsis)),
InkWell(
onTap: () {
provider.removeAttachment();
},
child: const Icon(Icons.close, size: 18, color: Colors.red),
),
],
),
),
],
],
),
);
}
Widget _buildDescriptionField(EtqanOvrProviderModel provider) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all(color: const Color(0xffefefef), width: 1)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: <Widget>[
Text(LocaleKeys.description.tr(), style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.44)),
const Text(' *', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Colors.red, letterSpacing: -0.44)),
],
),
GestureDetector(
onTap:
provider.speechEnabled
? () {
String localeId = AppState().isArabic(context) ? 'ar_SA' : 'en_US';
provider.toggleListening(localeId, _descriptionController);
}
: null,
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(color: provider.isListening ? MyColors.gradiantEndColor.withOpacity(0.1) : Colors.transparent, borderRadius: BorderRadius.circular(20)),
child: Icon(
provider.isListening ? Icons.mic : Icons.mic_none,
color: provider.isListening ? MyColors.gradiantEndColor : (provider.speechEnabled ? MyColors.grey57Color : Colors.grey.shade300),
size: 24,
),
),
),
],
),
if (provider.isListening)
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: <Widget>[
Container(width: 8, height: 8, decoration: BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(4))),
const SizedBox(width: 8),
const Text('Listening...', style: TextStyle(fontSize: 12, color: MyColors.gradiantEndColor, fontWeight: FontWeight.w500)),
],
),
),
TextField(
controller: _descriptionController,
maxLines: 4,
onChanged: (String value) => provider.setDescription(value),
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: Color(0xff2B353E), letterSpacing: -0.44),
decoration: InputDecoration(
isDense: true,
hintText: LocaleKeys.writeAMessage.tr(),
hintStyle: const TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: Color(0xff575757), letterSpacing: -0.56),
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
),
),
],
),
);
}
Widget _buildAnonymousCheckbox(EtqanOvrProviderModel provider) {
return InkWell(
onTap: () {
provider.setIsAnonymous(!provider.isAnonymous);
},
child: Row(
children: <Widget>[
Checkbox(
value: provider.isAnonymous,
onChanged: (bool? value) {
if (value != null) {
provider.setIsAnonymous(value);
}
},
activeColor: MyColors.gradiantEndColor,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
),
Expanded(child: Text(LocaleKeys.submitAsAnonymous.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xff2B353E), letterSpacing: -0.44))),
],
),
);
}
}

@ -1,165 +0,0 @@
import 'package:easy_localization/easy_localization.dart' as lclize;
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/config/routes.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/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_requests_model.dart';
import 'package:mohem_flutter_app/provider/etqan_ovr_provider.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:provider/provider.dart';
import 'package:intl/src/intl/text_direction.dart' as intl;
import 'package:sizer/sizer.dart';
class EtqanOvrHome extends StatefulWidget {
const EtqanOvrHome({Key? key}) : super(key: key);
@override
_EtqanOvrHomeState createState() => _EtqanOvrHomeState();
}
class _EtqanOvrHomeState extends State<EtqanOvrHome> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<EtqanOvrProviderModel>().fetchEtqanEmployeeRequests(context);
});
}
Color _getStatusColor(String status) {
String statusLower = status.toLowerCase();
if (statusLower.contains('open') || statusLower.contains('مفتوح')) {
return const Color(0xff2196F3); // Blue for open
} else if (statusLower.contains('progress') || statusLower.contains('قيد التنفيذ')) {
return const Color(0xffFF9800); // Orange for in progress
} else if (statusLower.contains('closed') || statusLower.contains('مغلق')) {
return const Color(0xff4CAF50); // Green for closed
}
return MyColors.grey70Color; // Default gray
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBarWidget(context, title: LocaleKeys.myIncidents.tr()),
body: Consumer<EtqanOvrProviderModel>(
builder: (BuildContext context, EtqanOvrProviderModel provider, Widget? child) {
return Column(
children: <Widget>[
(provider.getEtqanEmployeeRequestsList == null || provider.getEtqanEmployeeRequestsList!.isEmpty
? Utils.getNoDataWidget(context)
: ListView.separated(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.all(20),
itemBuilder: (BuildContext context, int index) {
EtqanGetEmployeeOvrRequestsResponse data = provider.getEtqanEmployeeRequestsList![index];
return InkWell(
onTap: () {
context.read<EtqanOvrProviderModel>().setTicketId(data.ticketNumber ?? '', status: data.status, statusAr: data.statusAr, isAnonymous: data.isAnonymous);
Navigator.pushNamed(context, AppRoutes.etqanGetIncidientRequest);
},
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15), border: Border.all(color: const Color(0xffefefef), width: 1)),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: [
Expanded(
child: Wrap(
spacing: 8,
runSpacing: 8,
children: [
if (data.status != null && data.status!.isNotEmpty)
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: _getStatusColor(data.status ?? "").withOpacity(0.1),
borderRadius: BorderRadius.circular(6),
border: Border.all(color: _getStatusColor(data.status ?? "").withOpacity(0.3), width: 1),
),
child: (AppState().isArabic(context) ? (data.statusAr ?? "") : (data.status ?? "")).toText11(
color: _getStatusColor(data.status ?? ""),
isBold: true,
),
),
if (data.isAnonymous == true)
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: MyColors.grey70Color.withOpacity(0.15),
borderRadius: BorderRadius.circular(6),
border: Border.all(color: MyColors.grey70Color.withOpacity(0.3), width: 1),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.visibility_off, size: 12, color: MyColors.grey70Color),
const SizedBox(width: 4),
(AppState().isArabic(context) ? "مجهول" : "Anonymous").toText11(color: MyColors.grey70Color, isBold: true),
],
),
),
],
),
),
const SizedBox(width: 8),
],
),
8.height,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
(data.ticketNumber ?? "").toText14(isBold: true, color: MyColors.darkTextColor),
Directionality(textDirection: TextDirection.ltr, child: (data.createdDate!.split(" ").first ?? "").toText12(color: MyColors.grey70Color)),
],
),
8.height,
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [(data.description ?? "").toText12(color: MyColors.grey57Color).expanded]),
3.height,
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
children: [
RotatedBox(
quarterTurns: AppState().isArabic(context) ? 2 : 0,
child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.grey70Color, width: 16, height: 16),
),
],
),
],
),
),
],
),
),
);
},
separatorBuilder: (BuildContext context, int index) => 12.height,
itemCount: provider.getEtqanEmployeeRequestsList!.length,
))
.expanded,
DefaultButton(LocaleKeys.reportIncident.tr(), () async {
await Navigator.pushNamed(context, AppRoutes.etqanCreateRequest);
// getOpenTickets();
}).insideContainer,
],
);
},
),
);
}
}

@ -1,235 +0,0 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.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/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_employee_incident_report.dart';
import 'package:mohem_flutter_app/provider/etqan_ovr_provider.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:provider/provider.dart';
import 'package:open_filex/open_filex.dart';
import 'package:path_provider/path_provider.dart';
class EtqanOvrRequestDetailed extends StatefulWidget {
const EtqanOvrRequestDetailed({Key? key}) : super(key: key);
@override
_EtqanOvrRequestDetailedState createState() => _EtqanOvrRequestDetailedState();
}
class _EtqanOvrRequestDetailedState extends State<EtqanOvrRequestDetailed> {
List<String> get fieldOrder => ['Status', 'Project', 'Created', 'Is Anonymous', 'Occurrence Time', 'OVR Reference No', 'Involved Employee Number', 'Description'];
List<String> get hiddenFields => ['OVR Id'];
Color _getStatusColor(String status) {
String statusLower = status.toLowerCase();
if (statusLower.contains('open') || statusLower.contains('مفتوح')) {
return const Color(0xff2196F3); // Blue for open
} else if (statusLower.contains('progress') || statusLower.contains('قيد التنفيذ')) {
return const Color(0xffFF9800); // Orange for in progress
} else if (statusLower.contains('closed') || statusLower.contains('مغلق')) {
return const Color(0xff4CAF50); // Green for closed
}
return MyColors.grey70Color; // Default gray
}
Future<void> _viewAttachment(BuildContext context, String base64Data) async {
try {
Uint8List bytes = base64.decode(base64Data);
String dir = (await getApplicationDocumentsDirectory()).path;
String ext = 'pdf';
if (base64Data.startsWith('/9j/')) {
ext = 'jpg';
} else if (base64Data.startsWith('iVBORw0KGgo')) {
ext = 'png';
}
File file = File("$dir/${DateTime.now().millisecondsSinceEpoch}.$ext");
await file.writeAsBytes(bytes);
OpenFilex.open(file.path);
} catch (e) {
Utils.showToast("Cannot open attachment.");
}
}
List<EtqanGetIncidentRequestResponse> _getOrderedList(List<EtqanGetIncidentRequestResponse> list, EtqanOvrProviderModel provider) {
List<EtqanGetIncidentRequestResponse> orderedList = [];
List<EtqanGetIncidentRequestResponse> filteredList = list.where((EtqanGetIncidentRequestResponse item) => !hiddenFields.any((f) => f.toLowerCase() == item.key?.toLowerCase())).toList();
for (String fieldName in fieldOrder) {
// Check if this is Status or Is Anonymous - inject from provider
if (fieldName.toLowerCase() == 'status' && provider.getCurrentStatus.isNotEmpty) {
orderedList.add(EtqanGetIncidentRequestResponse(
key: 'Status',
keyAr: 'الحالة',
value: provider.getCurrentStatus,
valueAr: provider.getCurrentStatusAr.isNotEmpty ? provider.getCurrentStatusAr : provider.getCurrentStatus,
));
} else if (fieldName.toLowerCase() == 'is anonymous') {
orderedList.add(EtqanGetIncidentRequestResponse(
key: 'Is Anonymous',
keyAr: 'مجهول',
value: provider.getCurrentIsAnonymous.toString(),
valueAr: provider.getCurrentIsAnonymous.toString(),
));
} else {
// Regular field from API
var item = filteredList.firstWhere((e) => e.key?.toLowerCase() == fieldName.toLowerCase(), orElse: () => EtqanGetIncidentRequestResponse());
if (item.key != null) {
orderedList.add(item);
}
}
}
for (var item in filteredList) {
if (!fieldOrder.any((f) => f.toLowerCase() == item.key?.toLowerCase())) {
orderedList.add(item);
}
}
return orderedList;
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
EtqanOvrProviderModel provider = context.read<EtqanOvrProviderModel>();
provider.fetchEtqanEmployeeIncidentRequests(context, provider.getTicketId);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBarWidget(context, title: LocaleKeys.incidentDetails.tr()),
body: Consumer<EtqanOvrProviderModel>(
builder: (BuildContext context, EtqanOvrProviderModel provider, Widget? child) {
if (provider.getEtqanEmployeeIncidnetRequest == null || provider.getEtqanEmployeeIncidnetRequest!.isEmpty) {
return Utils.getNoDataWidget(context);
}
List<EtqanGetIncidentRequestResponse> orderedList = _getOrderedList(provider.getEtqanEmployeeIncidnetRequest!, provider);
return SingleChildScrollView(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.all(16),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
border: Border.all(color: const Color(0xffefefef), width: 1),
boxShadow: <BoxShadow>[BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 10, offset: const Offset(0, 2))],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:
orderedList.asMap().entries.map((MapEntry<int, EtqanGetIncidentRequestResponse> entry) {
int index = entry.key;
EtqanGetIncidentRequestResponse data = entry.value;
bool isLast = index == orderedList.length - 1;
bool isAttachment = data.key?.toLowerCase() == 'attachment';
bool isStatus = data.key?.toLowerCase() == 'status';
bool isAnonymous = data.key?.toLowerCase() == 'is anonymous';
String displayKey = context.locale.languageCode == "ar" ? (data.keyAr ?? data.key ?? "") : (data.key ?? "");
String displayValue = context.locale.languageCode == "ar" ? (data.valueAr ?? data.value ?? "") : (data.value ?? "");
bool hasSubValues = data.subValues != null && data.subValues!.isNotEmpty;
return Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(flex: 2, child: displayKey.toText12(isBold: true, color: MyColors.grey57Color)),
8.width,
Expanded(
flex: 3,
child: isStatus
? displayValue.toText14(
color: _getStatusColor(displayValue),
isBold: true,
)
: isAnonymous
? (displayValue.toLowerCase() == 'true' || displayValue.toLowerCase() == 'yes' || displayValue == '1'
? (context.locale.languageCode == "ar" ? "نعم" : "Yes")
: (context.locale.languageCode == "ar" ? "لا" : "No")
).toText14(color: MyColors.darkTextColor)
: isAttachment && hasSubValues
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: data.subValues!.asMap().entries.map((entry) {
int subIndex = entry.key;
String base64Data = entry.value;
return Padding(
padding: EdgeInsets.only(bottom: subIndex < data.subValues!.length - 1 ? 8.0 : 0),
child: GestureDetector(
onTap: () => _viewAttachment(context, base64Data),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: context.locale.languageCode == "ar" ? MainAxisAlignment.end : MainAxisAlignment.start,
children: [
Text(
data.subValues!.length > 1 ? '${LocaleKeys.viewAttachment.tr()} ${subIndex + 1}' : LocaleKeys.viewAttachment.tr(),
style: const TextStyle(
fontSize: 14,
color: MyColors.green9CColor,
decoration: TextDecoration.underline,
decorationColor: MyColors.green9CColor,
),
),
],
),
),
);
}).toList(),
)
: hasSubValues
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (displayValue.isNotEmpty) displayValue.toText14(color: MyColors.darkTextColor),
if (displayValue.isNotEmpty) 8.height,
...data.subValues!
.map(
(subValue) => Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [const Text("• ", style: TextStyle(fontSize: 14)), Expanded(child: subValue.toText14(color: MyColors.darkTextColor))],
),
),
)
.toList(),
],
)
: (displayValue.isNotEmpty ? displayValue : "-").toText14(color: MyColors.darkTextColor),
),
],
),
),
if (!isLast) const Padding(padding: EdgeInsets.only(left: 14, right: 14), child: Divider(height: 1, thickness: 1, color: Color(0xffefefef))),
],
);
}).toList(),
),
),
);
},
).paddingOnly(bottom: 20),
);
}
}

@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:io';
import 'dart:ui' as ui;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_countdown_timer/flutter_countdown_timer.dart';
@ -33,7 +34,6 @@ import 'package:mohem_flutter_app/widgets/shimmer/offers_shimmer_widget.dart';
import 'package:provider/provider.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:signalr_netcore/signalr_client.dart';
import 'package:url_launcher/url_launcher.dart';
late HubConnection chatHubConnection;
@ -156,7 +156,6 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
data.fetchMenuEntries();
data.fetchEventActivity();
data.getCategoryOffersListAPI(context);
data.fetchGreetingCards();
marathonProvider.getMarathonDetailsFromApi();
marathonProvider.getMarathonTutorial();
if (isFromInit) {
@ -165,12 +164,11 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
if (!cProvider.disbaleChatForThisUser && !isFromInit) checkHubCon();
_refreshController.refreshCompleted();
await data.fetchTicketBooking();
// await data.fetchTicketBooking();
// if (data.ticketBookingResponse != null && !data.ticketBookingResponse!.success) {
// data.fetchTicketBalance();
// // data.fetchTicketBalance();
// }
//continue here
}
@ -185,18 +183,13 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
ItgMainRes? response = await DashboardApiClient().getAdvertisementDetail(list[ermIndex].notificationMasterId ?? "");
// Utils.hideLoading(context);
if (response!.mohemmItgResponseItem!.result!.data != null) {
for (ItgResponseData payload in response.mohemmItgResponseItem!.result!.data!) {
if (payload.notificationMasterId == list[ermIndex].notificationMasterId) {
payload.deadline = list[ermIndex].deadline;
}
}
if (list[ermIndex].notificationType == "Survey") {
await Navigator.pushNamed(context, AppRoutes.survey, arguments: response.mohemmItgResponseItem!.result!.data!.first);
} else {
await Navigator.pushNamed(
context,
AppRoutes.advertisement,
arguments: {"masterId": list[ermIndex].notificationMasterId, "advertisement": response.mohemmItgResponseItem!.result!.data!.first},
arguments: {"masterId": list[ermIndex].notificationMasterId, "advertisement": response.mohemmItgResponseItem!.result!.data!.first.advertisement},
);
}
}
@ -290,9 +283,7 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
});
},
),
Image
.asset("assets/images/logos/main_mohemm_logo.png", width: 134, height: 28)
.expanded,
Image.asset("assets/images/logos/main_mohemm_logo.png", width: 134, height: 28).expanded,
SvgPicture.asset("assets/images/announcements.svg", matchTextDirection: true).onPress(() async {
await Navigator.pushNamed(context, AppRoutes.announcements);
}),
@ -324,103 +315,103 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
child: Consumer<DashboardProviderModel>(
builder: (BuildContext context, DashboardProviderModel model, Widget? child) {
return (model.isAttendanceTrackingLoading
? GetAttendanceTrackingShimmer()
: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
gradient: const LinearGradient(
transform: GradientRotation(.46),
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [MyColors.gradiantEndColor, MyColors.gradiantStartColor],
),
),
child: Stack(
alignment: Alignment.center,
children: [
if (model.isTimeRemainingInSeconds == 0) SvgPicture.asset("assets/images/thumb.svg"),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true),
if (model.isTimeRemainingInSeconds == 0) DateTime.now().toString().split(" ")[0].toText12(color: Colors.white),
if (model.isTimeRemainingInSeconds != 0)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
? GetAttendanceTrackingShimmer()
: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
gradient: const LinearGradient(
transform: GradientRotation(.46),
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [MyColors.gradiantEndColor, MyColors.gradiantStartColor],
),
),
child: Stack(
alignment: Alignment.center,
children: [
if (model.isTimeRemainingInSeconds == 0) SvgPicture.asset("assets/images/thumb.svg"),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true),
if (model.isTimeRemainingInSeconds == 0) DateTime.now().toString().split(" ")[0].toText12(color: Colors.white),
if (model.isTimeRemainingInSeconds != 0)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
9.height,
Directionality(
textDirection: ui.TextDirection.ltr,
child: CountdownTimer(
endTime: model.endTime,
onEnd: null,
endWidget: "00:00:00".toText14(color: Colors.white, isBold: true),
textStyle: const TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold),
),
),
LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white),
9.height,
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(20)),
child: LinearProgressIndicator(
value: model.progress,
minHeight: 8,
valueColor: const AlwaysStoppedAnimation<Color>(Colors.white),
backgroundColor: const Color(0xff196D73),
),
),
],
),
],
).paddingOnly(top: 12, right: 15, left: 12),
),
Row(
children: [
9.height,
Directionality(
textDirection: ui.TextDirection.ltr,
child: CountdownTimer(
endTime: model.endTime,
onEnd: null,
endWidget: "00:00:00".toText14(color: Colors.white, isBold: true),
textStyle: const TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold),
),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.checkIn.tr().toText12(color: Colors.white),
(model.attendanceTracking!.pSwipeIn == null ? "--:--" : model.attendanceTracking!.pSwipeIn).toString().toText14(
color: Colors.white,
isBold: true,
),
4.height,
],
).paddingOnly(left: 12, right: 12),
),
LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white),
9.height,
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(20)),
child: LinearProgressIndicator(
value: model.progress,
minHeight: 8,
valueColor: const AlwaysStoppedAnimation<Color>(Colors.white),
backgroundColor: const Color(0xff196D73),
Container(
margin: EdgeInsets.only(top: AppState().isArabic(context) ? 6 : 0),
width: 45,
height: 45,
padding: const EdgeInsets.only(left: 10, right: 10),
decoration: BoxDecoration(
color: const Color(0xff259EA4),
borderRadius: BorderRadius.only(
bottomRight: AppState().isArabic(context) ? const Radius.circular(0) : const Radius.circular(15),
bottomLeft: AppState().isArabic(context) ? const Radius.circular(15) : const Radius.circular(0),
),
),
),
child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/biometrics.svg" : "assets/images/biometrics.svg"),
).onPress(() {
showMyBottomSheet(context, callBackFunc: () {}, child: MarkAttendanceWidget(model, isFromDashboard: true));
}),
],
),
],
).paddingOnly(top: 12, right: 15, left: 12),
),
Row(
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.checkIn.tr().toText12(color: Colors.white),
(model.attendanceTracking!.pSwipeIn == null ? "--:--" : model.attendanceTracking!.pSwipeIn).toString().toText14(
color: Colors.white,
isBold: true,
),
4.height,
],
).paddingOnly(left: 12, right: 12),
],
),
Container(
margin: EdgeInsets.only(top: AppState().isArabic(context) ? 6 : 0),
width: 45,
height: 45,
padding: const EdgeInsets.only(left: 10, right: 10),
decoration: BoxDecoration(
color: const Color(0xff259EA4),
borderRadius: BorderRadius.only(
bottomRight: AppState().isArabic(context) ? const Radius.circular(0) : const Radius.circular(15),
bottomLeft: AppState().isArabic(context) ? const Radius.circular(15) : const Radius.circular(0),
),
),
child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/biometrics.svg" : "assets/images/biometrics.svg"),
).onPress(() {
showMyBottomSheet(context, callBackFunc: () {}, child: MarkAttendanceWidget(model, isFromDashboard: true));
}),
],
),
],
),
],
),
).onPress(() {
Navigator.pushNamed(context, AppRoutes.todayAttendance);
}))
).onPress(() {
Navigator.pushNamed(context, AppRoutes.todayAttendance);
}))
.animatedSwither();
},
),
@ -432,29 +423,8 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
),
],
).paddingOnly(left: 21, right: 21, top: 7, bottom: 21),
Consumer<DashboardProviderModel>(
builder: (BuildContext context, DashboardProviderModel model, Widget? child) {
if (model.isGreetingCardsLoading) {
return const GreetingCardShimmer();
} else if (model.isDisplayEidGreetings && model.greetingCardsList != null && model.greetingCardsList!.isNotEmpty) {
return SizedBox(
width: double.infinity,
child: SvgPicture.network(
AppState().isArabic(context)
? (model.greetingCardsList!.first.backgroundImageUrlAr ?? "https://www.hmg.com/Lists/CS_Greeting_Cards/Attachments/5/CS_Ramadan_26.jpg")
: (model.greetingCardsList!.first.backgroundImageUrlEn ?? "https://www.hmg.com/Lists/CS_Greeting_Cards/Attachments/5/CS_Ramadan_26.jpg"),
fit: BoxFit.cover,
placeholderBuilder: (context) => ClipRRect(borderRadius: BorderRadius.circular(18), child: Container(height: 80, color: Colors.white).toShimmer()),
).onPress(() {
launchUrl(Uri.parse(AppState().isArabic(context) ? model.greetingCardsList!.first.urlAr! : model.greetingCardsList!.first.urlEn!));
}),
);
} else {
return const SizedBox();
}
},
).paddingOnly(bottom: 24, left: 24, right: 24),
// eventActivityWidget(context),
eventActivityWidget(context),
if (isDisplayMazaya) ...[
Column(
mainAxisSize: MainAxisSize.min,
@ -490,70 +460,70 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
flex: 2,
child: RichText(
text:
AppState().isArabic(context)
? TextSpan(
children: [
TextSpan(
text: 'اطلع على مميزات',
style: TextStyle(
fontSize: 16,
letterSpacing: -0.2,
fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins',
fontWeight: FontWeight.w700,
height: 24 / 16,
color: const Color(0xFF5D5E5E),
),
),
TextSpan(
text: ' مزايا',
style: TextStyle(
fontSize: 16,
fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins',
fontWeight: FontWeight.w700,
letterSpacing: -0.2,
height: 24 / 16,
color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed
),
),
],
)
: TextSpan(
children: [
TextSpan(
text: LocaleKeys.explore.tr() + ' ',
style: const TextStyle(
fontSize: 16,
letterSpacing: -0.2,
fontFamily: 'Poppins',
fontWeight: FontWeight.w700,
height: 24 / 16,
color: Color(0xFF5D5E5E),
),
),
TextSpan(
text: LocaleKeys.mazaya.tr(),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
fontFamily: 'Poppins',
letterSpacing: -0.2,
height: 24 / 16,
color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed
),
),
TextSpan(
text: ' ' + LocaleKeys.benefits.tr(),
style: const TextStyle(
fontSize: 16,
letterSpacing: -0.2,
fontFamily: 'Poppins',
fontWeight: FontWeight.w700,
height: 24 / 16,
color: Color(0xFF5D5E5E),
),
),
],
),
AppState().isArabic(context)
? TextSpan(
children: [
TextSpan(
text: 'اطلع على مميزات',
style: TextStyle(
fontSize: 16,
letterSpacing: -0.2,
fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins',
fontWeight: FontWeight.w700,
height: 24 / 16,
color: Color(0xFF5D5E5E),
),
),
TextSpan(
text: ' مزايا',
style: TextStyle(
fontSize: 16,
fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins',
fontWeight: FontWeight.w700,
letterSpacing: -0.2,
height: 24 / 16,
color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed
),
),
],
)
: TextSpan(
children: [
TextSpan(
text: LocaleKeys.explore.tr() + ' ',
style: const TextStyle(
fontSize: 16,
letterSpacing: -0.2,
fontFamily: 'Poppins',
fontWeight: FontWeight.w700,
height: 24 / 16,
color: Color(0xFF5D5E5E),
),
),
TextSpan(
text: LocaleKeys.mazaya.tr(),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
fontFamily: 'Poppins',
letterSpacing: -0.2,
height: 24 / 16,
color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed
),
),
TextSpan(
text: ' ' + LocaleKeys.benefits.tr(),
style: const TextStyle(
fontSize: 16,
letterSpacing: -0.2,
fontFamily: 'Poppins',
fontWeight: FontWeight.w700,
height: 24 / 16,
color: Color(0xFF5D5E5E),
),
),
],
),
),
),
const Expanded(flex: 1, child: SizedBox()),
@ -633,42 +603,42 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
return model.isOffersLoading
? const OffersShimmerWidget()
: InkWell(
onTap: () {
navigateToDetails(data.getOffersList[index]);
},
child: SizedBox(
width: 73,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: 73,
height: 73,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(100)),
border: Border.all(color: MyColors.lightGreyE3Color, width: 1),
),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(50)),
child: Hero(
tag: "ItemImage" + data.getOffersList[index].offersDiscountId.toString()!,
transitionOnUserGestures: true,
child: Image.network(data.getOffersList[index].logo ?? "", fit: BoxFit.contain),
onTap: () {
navigateToDetails(data.getOffersList[index]);
},
child: SizedBox(
width: 73,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: 73,
height: 73,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(100)),
border: Border.all(color: MyColors.lightGreyE3Color, width: 1),
),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(50)),
child: Hero(
tag: "ItemImage" + data.getOffersList[index].offersDiscountId.toString()!,
transitionOnUserGestures: true,
child: Image.network(data.getOffersList[index].logo ?? "", fit: BoxFit.contain),
),
),
),
),
),
4.height,
Expanded(
child:
AppState().isArabic(context)
? data.getOffersList[index].titleAr!.toText12(isCenter: true, maxLine: 1)
: data.getOffersList[index].titleEn!.toText12(isCenter: true, maxLine: 1),
4.height,
Expanded(
child:
AppState().isArabic(context)
? data.getOffersList[index].titleAr!.toText12(isCenter: true, maxLine: 1)
: data.getOffersList[index].titleEn!.toText12(isCenter: true, maxLine: 1),
),
],
),
],
),
),
);
),
);
},
separatorBuilder: (BuildContext cxt, int index) => 8.width,
itemCount: 9,
@ -691,9 +661,7 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
mainAxisSize: MainAxisSize.min,
children: [
ServicesWidget(),
context
.watch<MarathonProvider>()
.isLoading ? const MarathonBannerShimmer().paddingAll(20) : const MarathonBanner().paddingOnly(left: 21, right: 21, bottom: 8, top: 8),
context.watch<MarathonProvider>().isLoading ? const MarathonBannerShimmer().paddingAll(20) : const MarathonBanner().paddingOnly(left: 21, right: 21, bottom: 8, top: 8),
// context.watch<MarathonProvider>().isTutorialLoading
// ? const MarathonBannerShimmer().paddingAll(20)
// : Container(
@ -797,28 +765,28 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
SvgPicture.asset(
"assets/icons/chat/chat.svg",
color:
!checkIfPrivilegedForChat()
? MyColors.lightGreyE3Color
: currentIndex == 4
? MyColors.grey3AColor
: cProvider.disbaleChatForThisUser
? MyColors.lightGreyE3Color
: MyColors.grey98Color,
!checkIfPrivilegedForChat()
? MyColors.lightGreyE3Color
: currentIndex == 4
? MyColors.grey3AColor
: cProvider.disbaleChatForThisUser
? MyColors.lightGreyE3Color
: MyColors.grey98Color,
).paddingAll(4),
Consumer<ChatProviderModel>(
builder: (BuildContext cxt, ChatProviderModel data, Widget? child) {
return !checkIfPrivilegedForChat()
? const SizedBox()
: Positioned(
right: 0,
top: 0,
child: Container(
padding: const EdgeInsets.only(left: 4, right: 4),
alignment: Alignment.center,
decoration: BoxDecoration(color: cProvider.disbaleChatForThisUser ? MyColors.pinkDarkColor : MyColors.redColor, borderRadius: BorderRadius.circular(17)),
child: data.chatUConvCounter.toString().toText10(color: Colors.white),
),
);
right: 0,
top: 0,
child: Container(
padding: const EdgeInsets.only(left: 4, right: 4),
alignment: Alignment.center,
decoration: BoxDecoration(color: cProvider.disbaleChatForThisUser ? MyColors.pinkDarkColor : MyColors.redColor, borderRadius: BorderRadius.circular(17)),
child: data.chatUConvCounter.toString().toText10(color: Colors.white),
),
);
},
),
],
@ -849,21 +817,27 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
},
),
),
floatingActionButton: FloatingActionButton.large(
onPressed: () {
Navigator.pushNamed(context, AppRoutes.chatBotHome);
},
backgroundColor: Colors.transparent,
elevation: 0,
focusElevation: 0,
hoverElevation: 0,
highlightElevation: 0,
shape: const CircleBorder(),
child: SvgPicture.asset("assets/icons/chatbot.svg", width: 100, height: 100),
),
),
);
}
Widget eventActivityWidget(BuildContext context) {
return (context
.watch<DashboardProviderModel>()
.isEventLoadingLoading)
return (context.watch<DashboardProviderModel>().isEventLoadingLoading)
? const MarathonBannerShimmer().paddingOnly(left: 21, right: 21, bottom: 21, top: 0)
: (context
.watch<DashboardProviderModel>()
.eventActivity != null && context
.watch<DashboardProviderModel>()
.eventActivity!
.isActive == true)
: (context.watch<DashboardProviderModel>().eventActivity != null && context.watch<DashboardProviderModel>().eventActivity!.isActive == true)
? const EventActivityBanner().paddingOnly(left: 21, right: 21, bottom: 21, top: 0)
: const SizedBox();
}

@ -1,27 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter_countdown_timer/index.dart';
import 'package:jiffy/jiffy.dart';
import 'package:lottie/lottie.dart';
import 'package:mohem_flutter_app/api/dashboard_api_client.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/lottie_consts.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.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/widget_extensions.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart';
import 'package:mohem_flutter_app/models/itg/advertisement.dart' as ads;
import 'package:mohem_flutter_app/models/itg/itg_response_model.dart' as itgres;
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/ui/my_attendance/services_menu_list_screen.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:mohem_flutter_app/widgets/my_video_progress_indicator.dart';
import 'package:provider/provider.dart';
import 'package:sizer/sizer.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:video_player/video_player.dart';
@ -34,7 +26,7 @@ class ITGAdsScreen extends StatefulWidget {
class _ITGAdsScreenState extends State<ITGAdsScreen> {
late Future<VideoPlayerController> _futureController;
VideoPlayerController? _controller;
late VideoPlayerController _controller;
bool skip = false;
bool isVideo = false;
bool isImage = false;
@ -45,7 +37,6 @@ class _ITGAdsScreenState extends State<ITGAdsScreen> {
// late File imageFile;
late String imageUrl;
itgres.ItgResponseData? advertisementPayload;
ads.Advertisement? advertisementData;
dynamic data;
String? masterID;
@ -56,7 +47,6 @@ class _ITGAdsScreenState extends State<ITGAdsScreen> {
final ValueNotifier<bool> hasTimerEnded = ValueNotifier(false);
late CountdownTimerController timerController;
CountdownTimerController? deadlineTimerController;
void checkFileType() {
if (advertisementData!.viewAttachFileColl!.first.contentType!.toLowerCase() == "text") {
@ -133,227 +123,34 @@ class _ITGAdsScreenState extends State<ITGAdsScreen> {
});
}
String getDeadlineText() {
if (advertisementPayload?.deadline == null) {
return "";
}
try {
// Parse the deadline string using Jiffy (format: MM/dd/yyyy hh:mm:ss aa)
Jiffy deadlineJiffy = Jiffy.parse(advertisementPayload!.deadline!, pattern: "MM/dd/yyyy hh:mm:ss aa");
Jiffy nowJiffy = Jiffy.now();
// Calculate days left
int daysLeft = deadlineJiffy.diff(nowJiffy, unit: Unit.day).toInt();
if (daysLeft < 0) {
// Deadline has passed
int daysAgo = daysLeft.abs();
if (AppState().isArabic(context)) {
return "انتهى الموعد النهائي منذ $daysAgo ${daysAgo == 1 ? 'يوم' : 'أيام'}";
} else {
return "Deadline passed $daysAgo ${daysAgo == 1 ? 'day' : 'days'} ago";
}
} else if (daysLeft == 0) {
// Deadline is today
if (AppState().isArabic(context)) {
return "الموعد النهائي اليوم!";
} else {
return "Deadline is today!";
}
} else if (daysLeft == 1) {
// Deadline is tomorrow
if (AppState().isArabic(context)) {
return "الموعد النهائي غداً";
} else {
return "Deadline is tomorrow";
}
} else {
// Deadline is in the future
if (AppState().isArabic(context)) {
return "متبقي $daysLeft ${daysLeft <= 10 ? 'أيام' : 'يوم'}";
} else {
return "$daysLeft ${daysLeft == 1 ? 'day' : 'days'} left";
}
}
} catch (e) {
logger.e("Error parsing deadline: $e");
return "Deadline: ${Utils.formatDate(advertisementPayload!.deadline)}";
}
}
void initDeadlineTimer() {
if (advertisementPayload?.deadline == null) return;
try {
// Parse the deadline string using Jiffy (format: MM/dd/yyyy hh:mm:ss aa)
Jiffy deadlineJiffy = Jiffy.parse(advertisementPayload!.deadline!, pattern: "MM/dd/yyyy hh:mm:ss aa");
DateTime deadlineDate = deadlineJiffy.dateTime;
int endTime = deadlineDate.millisecondsSinceEpoch;
int currentTime = DateTime.now().millisecondsSinceEpoch;
// Only create timer if deadline is in the future
if (endTime > currentTime) {
deadlineTimerController = CountdownTimerController(
endTime: endTime,
onEnd: () {
// Timer ended, could trigger a refresh or notification
logger.d("Deadline timer ended");
},
);
}
} catch (e) {
logger.e("Error initializing deadline timer: $e");
}
}
@override
void dispose() {
_controller?.dispose();
deadlineTimerController?.dispose();
if (_controller != null) _controller.dispose();
super.dispose();
}
// Helper method to get localized text
String _getLocalizedText(String arabic, String english) {
return AppState().isArabic(context) ? arabic : english;
}
// Build advertisement content text
Widget _buildAdvertisementContent() {
String content = AppState().isArabic(context) ? advertisementData!.viewAttachFileColl![0].base64String! : advertisementData!.viewAttachFileColl![1].base64String!;
return content.toText16(color: MyColors.darkTextColor);
}
// Build deadline section with timer
Widget _buildDeadlineSection() {
if (deadlineTimerController != null) {
return Center(
child: Column(crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [_buildEndDateText(), 4.height, _buildTimeRemainingRow(), 8.height]),
);
} else {
return Center(child: _buildEndDateText(isLargeText: true));
}
}
// Build end date text
Widget _buildEndDateText({bool isLargeText = false}) {
String label = _getLocalizedText("تاريخ الانتهاء: ", "End Date: ");
String fullText = label + advertisementPayload!.deadline ?? "";
return isLargeText ? fullText.toText16(color: MyColors.darkTextColor) : fullText.toText14(color: MyColors.darkTextColor);
}
// Build time remaining row with timer
Widget _buildTimeRemainingRow() {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [_getLocalizedText("الوقت المتبقي", "Time Remaining").toText16(color: MyColors.darkTextColor, isBold: true), 8.width, _buildCountdownTimer()],
);
}
// Build countdown timer widget
Widget _buildCountdownTimer() {
return CountdownTimer(
controller: deadlineTimerController,
endWidget: _getLocalizedText("انتهى الموعد النهائي!", "Deadline Expired!").toText16(color: MyColors.redColor, isBold: true),
textStyle: const TextStyle(color: MyColors.darkTextColor, fontSize: 16, letterSpacing: -0.48, fontWeight: FontWeight.bold),
);
}
// Build action buttons list
Widget _buildActionButtons() {
return ListView.separated(
itemCount: advertisementData!.actionButtonsColl?.length ?? 0,
separatorBuilder: (cxt, index) => 16.height,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemBuilder: (cxt, index) {
var actionButton = advertisementData!.actionButtonsColl![index];
String btnText = _getLocalizedText(actionButton.btnTextAr, actionButton.btnTextEn);
return DefaultButton(btnText, () => _handleButtonAction(actionButton));
},
);
}
// Handle button action based on type
Future<void> _handleButtonAction(actionButton) async {
String actionValue = actionButton.actionValue.toLowerCase();
if (actionValue == "skip") {
Navigator.pop(context);
return;
}
String actionType = actionButton.actionType?.toLowerCase() ?? '';
if (actionType == 'redirect' && actionValue == 'external') {
await _handleExternalRedirect(actionButton.url);
} else if (actionType == 'redirect' && actionValue == 'internal') {
await _handleInternalRedirect(actionButton.url);
} else if (actionType == 'eit' && actionValue == 'internal') {
await _handleEitInternal(actionButton.url);
} else {
await _handleExternalRedirect(actionButton.iconOrImage);
}
}
// Handle external URL redirect
Future<void> _handleExternalRedirect(String url) async {
Uri uri = Uri.parse(url);
await launchUrl(uri, mode: LaunchMode.externalApplication).catchError((err) {
Utils.showToast('Could not launch');
});
Navigator.pop(context);
}
// Handle internal route navigation
Future<void> _handleInternalRedirect(String url) async {
await Navigator.pushNamed(context, url);
}
// Handle EIT internal navigation
Future<void> _handleEitInternal(String url) async {
var pro = Provider.of<DashboardProviderModel>(context, listen: false);
List<GetMenuEntriesList> menuList =
pro.getMenuEntriesList?.where((element) => element.parentMenuName == url && (element.menuEntryType == "FUNCTION" || element.menuEntryType == "MENU")).toList() ?? [];
Navigator.pushNamed(context, AppRoutes.servicesMenuListScreen, arguments: ServicesMenuListScreenParams(advertisementData!.advertisementTitle!, menuList));
}
@override
Widget build(BuildContext context) {
data = ModalRoute.of(context)!.settings.arguments;
advertisementPayload ??= data["advertisement"] as itgres.ItgResponseData;
advertisementData = advertisementPayload!.advertisement;
advertisementData ??= data["advertisement"] as ads.Advertisement;
masterID ??= data["masterId"];
if (advertisementData != null) {
checkFileType();
if (isTextURL) {
if (advertisementPayload?.deadline != null && deadlineTimerController == null) {
initDeadlineTimer();
}
} else {
videoDuration = advertisementData?.durationInSeconds ?? 0;
timerController = CountdownTimerController(endTime: DateTime.now().millisecondsSinceEpoch + 1000 * videoDuration, onEnd: onTimerEnd);
}
videoDuration = advertisementData?.durationInSeconds ?? 0;
timerController = CountdownTimerController(endTime: DateTime.now().millisecondsSinceEpoch + 1000 * videoDuration, onEnd: onTimerEnd);
}
return Scaffold(
backgroundColor: isTextURL ? Colors.white : Colors.black,
appBar:
isTextURL
? AppBar(
centerTitle: true,
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
title: (AppState().isArabic(context) ? advertisementData!.advertisementTitleAr! : advertisementData!.advertisementTitle!).toText24(color: MyColors.darkTextColor, isBold: true),
)
: null,
appBar: isTextURL
? AppBar(
centerTitle: true,
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
title: (AppState().isArabic(context) ? advertisementData!.advertisementTitleAr! : advertisementData!.advertisementTitle!).toText24(color: MyColors.darkTextColor, isBold: true),
//advertisementData!.viewAttachFileColl!.first.base64String!.toText24(color: MyColors.darkTextColor, isBold: true),
)
: null,
body: Stack(
children: [
if (isVideo)
@ -366,33 +163,38 @@ class _ITGAdsScreenState extends State<ITGAdsScreen> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child:
isAudio
? Lottie.asset(MyLottieConsts.audioPlaybackLottie)
: AspectRatio(
aspectRatio: _controller!.value.aspectRatio,
child: Stack(
alignment: Alignment.bottomCenter,
children: [
VideoPlayer(_controller!),
if (advertisementData?.isOptional ?? false)
Positioned(
top: 4,
right: 8,
child: Container(
padding: const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
decoration: BoxDecoration(color: Colors.white.withOpacity(0.5), borderRadius: BorderRadius.circular(30)),
child: Row(mainAxisSize: MainAxisSize.min, children: [Text(AppState().isArabic(context) ? "يتخطى" : "Skip"), Icon(Icons.skip_next)]),
).onPress(() {
Navigator.pop(context);
DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, "Skip").then((value) {
logger.d(value);
});
}),
),
],
),
child: isAudio
? Lottie.asset(MyLottieConsts.audioPlaybackLottie)
: AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: Stack(
alignment: Alignment.bottomCenter,
children: [
VideoPlayer(_controller),
if (advertisementData?.isOptional ?? false)
Positioned(
top: 4,
right: 8,
child: Container(
padding: const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
decoration: BoxDecoration(color: Colors.white.withOpacity(0.5), borderRadius: BorderRadius.circular(30)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(AppState().isArabic(context) ? "يتخطى" : "Skip"),
Icon(Icons.skip_next),
],
),
).onPress(() {
Navigator.pop(context);
DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, "Skip").then((value) {
logger.d(value);
});
}),
),
],
),
),
),
30.height,
CountdownTimer(
@ -428,7 +230,7 @@ class _ITGAdsScreenState extends State<ITGAdsScreen> {
alignment: Alignment.center,
children: [
MyVideoProgressIndicator(
_controller!,
_controller,
allowScrubbing: false,
//padding: EdgeInsets.only(left: 60, right: 60),
),
@ -436,13 +238,13 @@ class _ITGAdsScreenState extends State<ITGAdsScreen> {
btnText!,
hasTimerEndedBool
? () async {
Navigator.pop(context);
DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, advertisementData?.actionButtonsColl![index].actionValue).then((
value,
) {
logger.d(value);
});
}
Navigator.pop(context);
DashboardApiClient()
.setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, advertisementData?.actionButtonsColl![index].actionValue)
.then((value) {
logger.d(value);
});
}
: null,
disabledColor: Colors.transparent,
textColor: !hasTimerEndedBool ? Colors.white.withOpacity(.5) : Colors.white,
@ -450,12 +252,15 @@ class _ITGAdsScreenState extends State<ITGAdsScreen> {
],
);
}
return DefaultButton(btnText!, () async {
Navigator.pop(context);
DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, advertisementData?.actionButtonsColl![index].actionValue).then((value) {
logger.d(value);
});
}).paddingOnly(left: 60.0, right: 60.0, top: 8, bottom: 8);
return DefaultButton(
btnText!,
() async {
Navigator.pop(context);
DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, advertisementData?.actionButtonsColl![index].actionValue).then((value) {
logger.d(value);
});
},
).paddingOnly(left: 60.0, right: 60.0, top: 8, bottom: 8);
},
// gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: 1,
@ -528,7 +333,9 @@ class _ITGAdsScreenState extends State<ITGAdsScreen> {
],
);
} else {
return const Center(child: CircularProgressIndicator());
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
@ -541,17 +348,19 @@ class _ITGAdsScreenState extends State<ITGAdsScreen> {
children: [
Image.network(imageUrl),
50.height,
Container(padding: const EdgeInsets.all(16), decoration: Utils.containerRadius(MyColors.white, 10), child: const Icon(Icons.thumb_up, color: MyColors.gradiantEndColor)).onPress(() {
try {
Navigator.pop(context);
DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, "Like").then((value) {
logger.d(value);
});
} catch (ex) {
logger.wtf(ex);
Utils.handleException(ex, context, null);
}
}),
Container(padding: const EdgeInsets.all(16), decoration: Utils.containerRadius(MyColors.white, 10), child: const Icon(Icons.thumb_up, color: MyColors.gradiantEndColor)).onPress(
() {
try {
Navigator.pop(context);
DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, "Like").then((value) {
logger.d(value);
});
} catch (ex) {
logger.wtf(ex);
Utils.handleException(ex, context, null);
}
},
),
],
),
),
@ -562,11 +371,35 @@ class _ITGAdsScreenState extends State<ITGAdsScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
_buildAdvertisementContent(),
(AppState().isArabic(context) ? advertisementData!.viewAttachFileColl![0].base64String! : advertisementData!.viewAttachFileColl![1].base64String!)
.toText16(color: MyColors.darkTextColor),
24.height,
if (advertisementPayload != null && advertisementPayload!.deadline != null) _buildDeadlineSection(),
if (advertisementPayload != null && advertisementPayload!.deadline != null) 24.height,
_buildActionButtons(),
ListView.separated(
itemCount: advertisementData!.actionButtonsColl?.length ?? 0,
separatorBuilder: (cxt, index) => 16.height,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemBuilder: (cxt, index) => DefaultButton(
AppState().isArabic(context) ? advertisementData!.actionButtonsColl![index].btnTextAr : advertisementData!.actionButtonsColl![index].btnTextEn,
() async {
if (advertisementData!.actionButtonsColl![index].actionValue.toLowerCase() == "skip") {
Navigator.pop(context);
} else {
Uri uri = Uri.parse(advertisementData!.actionButtonsColl![index].iconOrImage);
// if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication).catchError((err) {
// print(err);
Utils.showToast('Could not launch');
});
Navigator.pop(context);
// } else {
// Utils.showToast('Could not launch');
// }
}
},
),
),
],
),
),

@ -58,15 +58,15 @@ class _AppDrawerState extends State<AppDrawer> {
children: [
AppState().memberInformationList!.eMPLOYEEIMAGE == null
? SvgPicture.asset(
"assets/images/user.svg",
height: 52,
width: 52,
)
"assets/images/user.svg",
height: 52,
width: 52,
)
: CircleAvatar(
radius: 52 / 2,
backgroundImage: MemoryImage(Utils.dataFromBase64String(AppState().memberInformationList!.eMPLOYEEIMAGE!)),
backgroundColor: Colors.black,
),
radius: 52 / 2,
backgroundImage: MemoryImage(Utils.dataFromBase64String(AppState().memberInformationList!.eMPLOYEEIMAGE!)),
backgroundColor: Colors.black,
),
12.width,
Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -119,7 +119,7 @@ class _AppDrawerState extends State<AppDrawer> {
}),
menuItem("assets/images/drawer/employee_id.svg", LocaleKeys.employeeDigitalID.tr(), "", closeDrawer: false, onPress: () => showMDialog(context, child: EmployeeDigitialIdDialog())),
if (AppState().businessCardPrivilege)
menuItem("assets/images/drawer/view_business_card.svg", LocaleKeys.viewBusinessCard.tr(), "", closeDrawer: false, onPress: () => showMDialog(context, child: BusinessCardDialog(), isBusniessCard: true)),
menuItem("assets/images/drawer/view_business_card.svg", LocaleKeys.viewBusinessCard.tr(), "", closeDrawer: false, onPress: () => showMDialog(context, child: BusinessCardDialog(), isBusniessCard: true)),
menuItem("assets/images/drawer/logout.svg", LocaleKeys.logout.tr(), "", color: MyColors.redA3Color, closeDrawer: false, onPress: (){Utils.performLogout(context, chatData);}),
// menuItem("assets/images/drawer/logout.svg", LocaleKeys.logout.tr(), "", color: MyColors.redA3Color, closeDrawer: false, onPress: () {Navigator.pushNamed(context, AppRoutes.survey,);
],
@ -156,9 +156,9 @@ class _AppDrawerState extends State<AppDrawer> {
],
).paddingOnly(left: 21, top: 10, bottom: 10, right: 21).onPress(closeDrawer
? () async {
Navigator.pop(context);
Future.delayed(const Duration(microseconds: 200), onPress);
}
Navigator.pop(context);
Future.delayed(const Duration(microseconds: 200), onPress);
}
: onPress!);
}
@ -171,13 +171,13 @@ class _AppDrawerState extends State<AppDrawer> {
setState(() {});
}
// void performLogout() async {
// // AppState().isAuthenticated = false;
// // AppState().isLogged = false;
// // AppState().setPostParamsInitConfig();
// // chatData.disposeData();
// // // SharedPreferences prefs = await SharedPreferences.getInstance();
// // // await prefs.clear();
// // Navigator.pushNamedAndRemoveUntil(context, AppRoutes.login, (Route<dynamic> route) => false, arguments: null);
// }
// void performLogout() async {
// // AppState().isAuthenticated = false;
// // AppState().isLogged = false;
// // AppState().setPostParamsInitConfig();
// // chatData.disposeData();
// // // SharedPreferences prefs = await SharedPreferences.getInstance();
// // // await prefs.clear();
// // Navigator.pushNamedAndRemoveUntil(context, AppRoutes.login, (Route<dynamic> route) => false, arguments: null);
// }
}

@ -1,18 +1,13 @@
import 'package:easy_localization/src/public_ext.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:mohem_flutter_app/api/eit_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/eit/get_eit_transaction_model.dart';
import 'package:mohem_flutter_app/models/sso_auth_model.dart';
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart';
import 'package:mohem_flutter_app/ui/screens/ticket/ticket_detailed_screen.dart';
import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart';
import 'package:provider/provider.dart';
@ -122,29 +117,8 @@ class MenusWidget extends StatelessWidget {
),
],
).paddingOnly(left: 10, right: 10, bottom: 6, top: 6),
).onPress(() async {
// Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(LocaleKeys.ticketBalance.tr(), "HMG_TKT_NEW_EIT_SS"));
var pro = Provider.of<DashboardProviderModel>(context, listen: false);
// if (menuEntry.menuName == "HMG_TICKET_REQUESTS") {
Utils.showLoading(context);
//Ticket Work
if (pro.ticketBookingResponse != null && pro.ticketBookingResponse!.success) {
SSOAuthModel? ssoToken = await pro.fetchSSOAuthRedirection(clientID: pro.ticketBookingResponse!.clientId);
if (ssoToken != null) {
dynamic url = await pro.fetchURLRedirection(token: ssoToken.data!.accessToken!, refreshToken: ssoToken.data!.refreshToken!);
await pro.fetchTicketAccuralBalance(context, DateTime.now());
pro.ticketHistoryTransactionList = await EITApiClient().getEITTransactions("HMG_TICKET_ITENARY_HR_EIT_SS", isCompleteList: true);
Utils.hideLoading(context);
// Here Need Work
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => TicketDetailedScreen(url: url, jwtToken: ssoToken.data!.accessToken)));
}
//}
} else {
Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(LocaleKeys.ticketBalance.tr(), "HMG_TKT_NEW_EIT_SS"));
}
).onPress(() {
Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(LocaleKeys.ticketBalance.tr(), "HMG_TKT_NEW_EIT_SS"));
}),
],
);

@ -92,7 +92,7 @@ class ServicesWidget extends StatelessWidget {
aspectRatio: 105 / 105,
child:
data.isServicesMenusLoading
? const ServicesMenuShimmer()
? ServicesMenuShimmer()
: Container(
decoration: BoxDecoration(
color: Colors.white,
@ -254,8 +254,6 @@ class ServicesWidget extends StatelessWidget {
Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(menuEntry.prompt!, menuEntry.functionName!));
} else if (menuEntry.requestType == "PAYSLIP") {
Navigator.pushNamed(context, AppRoutes.monthlyPaySlip);
} else if (menuEntry.requestType == "CEI") {
Navigator.pushNamed(context, AppRoutes.childEducation, arguments: menuEntry);
}
} else {
if (menuEntry.menuName == "HMG_TICKET_REQUESTS") {
@ -264,10 +262,11 @@ class ServicesWidget extends StatelessWidget {
Utils.showLoading(context);
SSOAuthModel? ssoToken = await pro.fetchSSOAuthRedirection(clientID: pro.ticketBookingResponse!.clientId);
if (ssoToken != null) {
dynamic url = await pro.fetchURLRedirection(token: ssoToken.data!.accessToken!, refreshToken: ssoToken.data!.refreshToken!);
dynamic url = await pro.fetchURLRedirection(token: ssoToken.data!.accessToken!);
await pro.fetchTicketAccuralBalance(context, DateTime.now());
pro.ticketHistoryTransactionList = await EITApiClient().getEITTransactions("HMG_TICKET_ITENARY_HR_EIT_SS", isCompleteList: true);
Utils.hideLoading(context);
// Here Need Work
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => TicketDetailedScreen(url: url, jwtToken: ssoToken.data!.accessToken)));
@ -312,7 +311,7 @@ class ServicesWidget extends StatelessWidget {
padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13),
scrollDirection: Axis.horizontal,
itemBuilder: (cxt, index) {
return AspectRatio(aspectRatio: 105 / 105, child: const ServicesMenuShimmer());
return AspectRatio(aspectRatio: 105 / 105, child: ServicesMenuShimmer());
},
separatorBuilder: (cxt, index) => 9.width,
itemCount: 4,

@ -326,7 +326,7 @@ class _AddLeaveBalanceScreenState extends State<AddLeaveBalanceScreen> {
displayText = displayText.replaceAll(" 00:00:00", "");
}
if (displayText.contains("/")) {
displayText = DateFormat('yyyy-MM-dd', "en_US").format(DateFormat("yyyy/MM/dd", "en_US").parse(DateUtil.replaceArabicNumber(displayText)));
displayText = DateFormat('yyyy-MM-dd').format(DateFormat("yyyy/MM/dd").parse(displayText));
}
}
return DynamicTextFieldWidget(
@ -336,7 +336,11 @@ class _AddLeaveBalanceScreenState extends State<AddLeaveBalanceScreen> {
isEnable: false,
onTap: () async {
if ((getabsenceDffStructureList![index].eSERVICESDV?.pVALUECOLUMNNAME != null)) {
selectedDate = DateUtil.parseFlexibleDateTime(getabsenceDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME) ?? selectedDate;
if (getabsenceDffStructureList![index].isDefaultTypeIsCDPS) {
selectedDate = DateFormat("yyyy/MM/dd").parse(getabsenceDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME!.replaceAll('/"', '').replaceAll(" 00:00:00", ""));
} else {
selectedDate = DateTime.parse(getabsenceDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME!);
}
}
DateTime date = await _selectDate(context);
String dateString = date.toString().split(' ').first;
@ -349,14 +353,14 @@ class _AddLeaveBalanceScreenState extends State<AddLeaveBalanceScreen> {
pRETURNMSG: "null",
pRETURNSTATUS: getabsenceDffStructureList![index].dEFAULTVALUE,
pVALUECOLUMNNAME:
getabsenceDffStructureList![index].isDefaultTypeIsCDPS ? Utils.reverseFormatStandardDate(Utils.formatDate(dateString)) : DateFormat('yyyy-MM-ddThh:mm:ss.s', "en_US").format(date));
getabsenceDffStructureList![index].isDefaultTypeIsCDPS ? Utils.reverseFormatStandardDate(Utils.formatDate(dateString)) : DateFormat('yyyy-MM-ddThh:mm:ss.s').format(date));
} else {
eservicesdv = ESERVICESDV(
pIDCOLUMNNAME: dateString,
pRETURNMSG: "null",
pRETURNSTATUS: getabsenceDffStructureList![index].dEFAULTVALUE,
pVALUECOLUMNNAME:
getabsenceDffStructureList![index].isDefaultTypeIsCDPS ? Utils.reverseFormatStandardDate(Utils.formatDate(dateString)) : DateFormat('yyyy-MM-ddThh:mm:ss.s', "en_US").format(date));
getabsenceDffStructureList![index].isDefaultTypeIsCDPS ? Utils.reverseFormatStandardDate(Utils.formatDate(dateString)) : DateFormat('yyyy-MM-ddThh:mm:ss.s').format(date));
}
getabsenceDffStructureList![index].eSERVICESDV = eservicesdv;
setState(() {});
@ -370,7 +374,7 @@ class _AddLeaveBalanceScreenState extends State<AddLeaveBalanceScreen> {
).paddingOnly(bottom: 12);
} else if (model.fORMATTYPE == "Y") {
String displayText = model.eSERVICESDV?.pIDCOLUMNNAME ?? "";
if (getabsenceDffStructureList[index].isDefaultTypeIsCDPS) {
if (getabsenceDffStructureList![index].isDefaultTypeIsCDPS) {
displayText = Utils.reverseFormatDate(displayText);
// if (displayText.contains(" 00:00:00")) {
// displayText = displayText.replaceAll(" 00:00:00", "");
@ -386,7 +390,19 @@ class _AddLeaveBalanceScreenState extends State<AddLeaveBalanceScreen> {
isEnable: false,
onTap: () async {
if ((getabsenceDffStructureList![index].eSERVICESDV?.pVALUECOLUMNNAME != null)) {
selectedDate = DateUtil.parseFlexibleDateTime(getabsenceDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME) ?? selectedDate;
if (getabsenceDffStructureList![index].isDefaultTypeIsCDPS) {
String tempDate = getabsenceDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME!;
if (tempDate.contains("00:00:00")) {
tempDate = tempDate.replaceAll("00:00:00", '').trim();
}
if (tempDate.contains("/")) {
selectedDate = DateFormat("yyyy/MM/dd").parse(tempDate);
} else {
selectedDate = DateFormat("yyyy-MM-dd").parse(tempDate);
}
} else {
selectedDate = DateTime.parse(getabsenceDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME!);
}
}
DateTime date = await _selectDate(context);
String dateString = date.toString().split(' ').first;
@ -398,14 +414,14 @@ class _AddLeaveBalanceScreenState extends State<AddLeaveBalanceScreen> {
pRETURNMSG: "null",
pRETURNSTATUS: getabsenceDffStructureList![index].dEFAULTVALUE,
pVALUECOLUMNNAME:
getabsenceDffStructureList![index].isDefaultTypeIsCDPS ? Utils.reverseFormatStandardDate(Utils.formatDate(dateString)) : DateFormat('yyyy-MM-ddThh:mm:ss.s', "en_US").format(date));
getabsenceDffStructureList![index].isDefaultTypeIsCDPS ? Utils.reverseFormatStandardDate(Utils.formatDate(dateString)) : DateFormat('yyyy-MM-ddThh:mm:ss.s').format(date));
} else {
eservicesdv = ESERVICESDV(
pIDCOLUMNNAME: dateString,
pRETURNMSG: "null",
pRETURNSTATUS: getabsenceDffStructureList![index].dEFAULTVALUE,
pVALUECOLUMNNAME:
getabsenceDffStructureList![index].isDefaultTypeIsCDPS ? Utils.reverseFormatStandardDate(Utils.formatDate(dateString)) : DateFormat('yyyy-MM-ddThh:mm:ss.s', "en_US").format(date));
getabsenceDffStructureList![index].isDefaultTypeIsCDPS ? Utils.reverseFormatStandardDate(Utils.formatDate(dateString)) : DateFormat('yyyy-MM-ddThh:mm:ss.s').format(date));
}
getabsenceDffStructureList![index].eSERVICESDV = eservicesdv;
@ -496,7 +512,7 @@ class _AddLeaveBalanceScreenState extends State<AddLeaveBalanceScreen> {
displayText = displayText.replaceAll(" 00:00:00", "");
}
if (!displayText.contains("-")) {
displayText = DateFormat('yyyy-MM-dd', "en_US").format(DateFormat("yyyy/MM/dd", "en_US").parse(DateUtil.replaceArabicNumber(displayText)));
displayText = DateFormat('yyyy-MM-dd').format(DateFormat("yyyy/MM/dd").parse(displayText));
}
}
return DynamicTextFieldWidget(
@ -506,7 +522,11 @@ class _AddLeaveBalanceScreenState extends State<AddLeaveBalanceScreen> {
isEnable: false,
onTap: () async {
if ((getabsenceDffStructureList![index].eSERVICESDV?.pVALUECOLUMNNAME != null)) {
selectedDate = DateUtil.parseFlexibleDateTime(getabsenceDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME) ?? selectedDate;
if (getabsenceDffStructureList![index].isDefaultTypeIsCDPS) {
selectedDate = DateFormat("yyyy/MM/dd").parse(getabsenceDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME!.replaceAll('/"', '').replaceAll(" 00:00:00", ""));
} else {
selectedDate = DateTime.parse(getabsenceDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME!);
}
}
DateTime date = await _selectDate(context);
String dateString = date.toString().split(' ').first;
@ -516,7 +536,7 @@ class _AddLeaveBalanceScreenState extends State<AddLeaveBalanceScreen> {
pRETURNMSG: "null",
pRETURNSTATUS: getabsenceDffStructureList![index].dEFAULTVALUE,
pVALUECOLUMNNAME:
getabsenceDffStructureList![index].isDefaultTypeIsCDPS ? Utils.reverseFormatStandardDate(Utils.formatDate(dateString)) : DateFormat('yyyy-MM-ddThh:mm:ss.s', "en_US").format(date));
getabsenceDffStructureList![index].isDefaultTypeIsCDPS ? Utils.reverseFormatStandardDate(Utils.formatDate(dateString)) : DateFormat('yyyy-MM-ddThh:mm:ss.s').format(date));
getabsenceDffStructureList![index].eSERVICESDV = eservicesdv;
setState(() {});
// if (model.cHILDSEGMENTSVSSplited?.isNotEmpty ?? false) {
@ -537,14 +557,14 @@ class _AddLeaveBalanceScreenState extends State<AddLeaveBalanceScreen> {
if (getabsenceDffStructureList[index].mOBILEENABLED != "Y") return;
if ((getabsenceDffStructureList![index].eSERVICESDV?.pVALUECOLUMNNAME != null)) {
var timeString = DateUtil.replaceArabicNumber(getabsenceDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME).split(":");
selectedDate = DateTime(0, 0, 0, int.tryParse(timeString[0]) ?? 0, timeString.length > 1 ? (int.tryParse(timeString[1]) ?? 0) : 0);
var timeString = getabsenceDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME!.split(":");
selectedDate = DateTime(0, 0, 0, int.parse(timeString[0]), int.parse(timeString[1]));
//DateTime.parse(getabsenceDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME!);
}
TimeOfDay _time = await _selectTime(context);
DateTime tempTime = DateTime(0, 1, 1, _time.hour, _time.minute);
String time = DateFormat('HH:mm', "en_US").format(tempTime).trim();
String time = DateFormat('HH:mm').format(tempTime).trim();
// DateTime date1 = DateTime(date.year, date.month, date.day);
// getabsenceDffStructureList![index].fieldAnswer = date.toString();

@ -112,7 +112,7 @@ class _VerifyLastLoginScreenState extends State<VerifyLastLoginScreen> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [LocaleKeys.lastLoginDetails.tr().toText16(), DateUtil.formatDateToDate(DateUtil.convertStringToDate(mobileLoginInfoListModel!.editedOn ?? DateTime.now().toString()), false).toText12()],
children: [LocaleKeys.lastLoginDetails.tr().toText16(), DateUtil.formatDateToDate(DateUtil.convertStringToDate(mobileLoginInfoListModel?.editedOn ?? null), false).toText12()],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -121,7 +121,7 @@ class _VerifyLastLoginScreenState extends State<VerifyLastLoginScreen> {
LocaleKeys.verificationType.tr().toText10(color: MyColors.grey57Color),
getVerificationType(mobileLoginInfoListModel!.loginType!).toText12(),
const Expanded(child: SizedBox()),
DateUtil.formatDateToTime(DateUtil.convertStringToDate(mobileLoginInfoListModel!.editedOn!)).toText12(),
DateUtil.formatDateToTime(DateUtil.convertStringToDate(mobileLoginInfoListModel?.editedOn)).toText12(),
],
),
],
@ -208,7 +208,7 @@ class _VerifyLastLoginScreenState extends State<VerifyLastLoginScreen> {
Future<bool> loginWithFaceIDAndBiometrics() async {
IOSAuthMessages iosStrings = const IOSAuthMessages(cancelButton: 'cancel', goToSettingsButton: 'settings', goToSettingsDescription: 'Please set up your Touch ID.', lockOut: 'Please reenable your Touch ID');
IOSAuthMessages iosStrings = const IOSAuthMessages(cancelButton: 'cancel');
// IOSAuthMessages iosStrings = const IOSAuthMessages(
// cancelButton: 'cancel',
@ -219,9 +219,7 @@ class _VerifyLastLoginScreenState extends State<VerifyLastLoginScreen> {
bool authenticated = false;
try {
authenticated = await auth.authenticate(localizedReason: 'Scan your fingerprint to authenticate', options: const AuthenticationOptions(
useErrorDialogs: true, stickyAuth: true, biometricOnly: true,
),
authenticated = await auth.authenticate(localizedReason: 'Scan your fingerprint to authenticate',
authMessages: [
iosStrings,
const AndroidAuthMessages(),

@ -515,11 +515,11 @@ class _VerifyLoginScreenState extends State<VerifyLoginScreen> {
Future<bool> loginWithFaceIDAndBiometrics() async {
IOSAuthMessages iosStrings =
const IOSAuthMessages(cancelButton: 'cancel', goToSettingsButton: 'settings', goToSettingsDescription: 'Please set up your Touch ID.', lockOut: 'Please reenable your Touch ID');
const IOSAuthMessages(cancelButton: 'cancel',);
bool authenticated = false;
try {
authenticated = await auth.authenticate(localizedReason: 'Scan your fingerprint to authenticate',
options: const AuthenticationOptions(useErrorDialogs: true, stickyAuth: true, biometricOnly: true,), authMessages: [
authMessages: [
iosStrings,
const AndroidAuthMessages(),
],);

@ -30,18 +30,21 @@ class QuestionCard extends StatelessWidget {
width: double.infinity,
child: AppinioSwiper(
duration: const Duration(milliseconds: 400),
padding: EdgeInsets.zero,
swipeOptions: const SwipeOptions.all(),
isDisabled: true,
controller: provider.swiperController,
unswipe: (int index, AppinioSwiperDirection direction) {},
onSwipe: (int index, AppinioSwiperDirection direction) {},
cards: provider.cardContentList,
cardCount: provider.cardContentList.length,
cardBuilder: (BuildContext context, int index) {
return provider.cardContentList[index];
},
),
),
);
}
}
class CardContent extends StatelessWidget {
const CardContent({Key? key}) : super(key: key);

@ -97,8 +97,8 @@ class _SubordinateLeaveState extends State<SubordinateLeave> {
separatorBuilder: (BuildContext cxt, int index) => 12.height,
itemCount: getSubordinatesLeavesTotalList.length,
itemBuilder: (BuildContext context, int index) {
var diffDays = DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATEEND!)
.difference(DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATESTART!))
var diffDays = DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATEEND)
.difference(DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATESTART))
.inDays + 1;
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
@ -109,8 +109,8 @@ class _SubordinateLeaveState extends State<SubordinateLeave> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
getSubordinatesLeavesTotalList[index].eMPLOYEENAME!.toText16(),
ItemDetailView(LocaleKeys.from.tr(), DateUtil.getFormattedDate(DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATESTART!), "MMM dd yyyy")),
ItemDetailView(LocaleKeys.to.tr(), DateUtil.getFormattedDate(DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATEEND!), "MMM dd yyyy")),
ItemDetailView(LocaleKeys.from.tr(), DateUtil.getFormattedDate(DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATESTART), "MMM dd yyyy")),
ItemDetailView(LocaleKeys.to.tr(), DateUtil.getFormattedDate(DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATEEND), "MMM dd yyyy")),
ItemDetailView(LocaleKeys.numberDays.tr(), diffDays.toString()),
],
).expanded

@ -1,107 +0,0 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/api/dashboard_api_client.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/get_employee_parking_details_model.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
class ParkingQrScreen extends StatefulWidget {
const ParkingQrScreen({Key? key}) : super(key: key);
@override
_RequestDetailsState createState() => _RequestDetailsState();
}
class _RequestDetailsState extends State<ParkingQrScreen> {
GetEmployeeParking? qrData;
@override
void initState() {
super.initState();
getTicketDetails();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBarWidget(context, title: LocaleKeys.parkingQr.tr()),
body:
qrData?.getEmployeeParking == null
? Center(child: Text(LocaleKeys.noDataAvailable.tr()))
: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Center(
child: Container(
padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [BoxShadow(color: Colors.grey.withValues(alpha: 0.2), spreadRadius: 5, blurRadius: 10, offset: const Offset(0, 3))],
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// QR Code Image
if (qrData!.getEmployeeParking!.qrCodeImageBase64 != null && qrData!.getEmployeeParking!.qrCodeImageBase64!.isNotEmpty)
Image.memory(Utils.dataFromBase64String(qrData!.getEmployeeParking!.qrCodeImageBase64!), width: 300, height: 300, fit: BoxFit.contain)
else
Container(
width: 300,
height: 300,
color: MyColors.lightGreyEFColor,
child: Center(child: Text(LocaleKeys.noDataAvailable.tr(), style: const TextStyle(color: MyColors.grey67Color))),
),
10.height,
Container(height: 1, color: MyColors.lightGreyE3Color, margin: const EdgeInsets.symmetric(vertical: 10)),
20.height,
SizedBox(
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Employee Code
if (qrData!.getEmployeeParking!.employeeCode != null && qrData!.getEmployeeParking!.employeeCode!.isNotEmpty) ...[
Text(LocaleKeys.employeeNumber.tr(), style: const TextStyle(fontSize: 14, color: MyColors.grey67Color, fontWeight: FontWeight.w400)),
8.height,
Text(qrData!.getEmployeeParking!.employeeCode!, style: const TextStyle(fontSize: 18, color: MyColors.darkTextColor, fontWeight: FontWeight.w600)),
8.height,
],
if (qrData!.getEmployeeParking!.createdAt != null) ...[
Text(LocaleKeys.creationDate.tr(), style: const TextStyle(fontSize: 14, color: MyColors.grey67Color, fontWeight: FontWeight.w400)),
8.height,
Text(
DateFormat('dd MMM yyyy, hh:mm a').format(qrData!.getEmployeeParking!.createdAt!),
style: const TextStyle(fontSize: 16, color: MyColors.darkTextColor, fontWeight: FontWeight.w500),
),
],
],
),
),
],
),
),
),
),
),
);
}
void getTicketDetails() async {
try {
qrData = GetEmployeeParking();
Utils.showLoading(context);
qrData = await DashboardApiClient().getEmployeeParkingQrCode();
Utils.hideLoading(context);
setState(() {});
} catch (ex) {
Utils.hideLoading(context);
Utils.handleException(ex, context, null);
}
}
}

@ -1,6 +1,7 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:easy_localization/src/public_ext.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/api/leave_balance_api_client.dart';
@ -26,7 +27,7 @@ import 'package:mohem_flutter_app/widgets/dynamic_forms/dynamic_textfield_widget
class FamilyMemberScreenParams {
String? actionType;
int? relationID;
int relationID;
int? flag;
FamilyMemberScreenParams(this.relationID, {this.actionType, this.flag});
@ -76,7 +77,7 @@ class _AddUpdateFamilyMemberState extends State<AddUpdateFamilyMember> {
element?.getContactDetailsList = new GetContactDetailsList();
});
} else if (params!.actionType == "UPDATE") {
getContactDetailsList = await ProfileApiClient().getContactDetailsList(params!.relationID!);
getContactDetailsList = await ProfileApiClient().getContactDetailsList(params!.relationID);
getBasicDetDffStructureList?.forEach((element) {
element?.getContactDetailsList = getContactDetailsList?.singleWhere((userDetail) => userDetail?.aPPLICATIONCOLUMNNAME == element?.aPPLICATIONCOLUMNNAME);

@ -1,4 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:easy_localization/src/public_ext.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/api/profile_api_client.dart';
@ -13,6 +13,7 @@ import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart';
import 'package:mohem_flutter_app/models/get_employee_contacts.model.dart';
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/ui/profile/add_update_family_member.dart';
import 'package:mohem_flutter_app/ui/profile/profile_screen.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:provider/provider.dart';
@ -27,11 +28,22 @@ class FamilyMembers extends StatefulWidget {
class _FamilyMembersState extends State<FamilyMembers> {
List<GetEmployeeContactsList> getEmployeeContactsList = [];
int? relationId;
dynamic? relationId;
int? flag;
GetMenuEntriesList menuEntries = GetMenuEntriesList();
@override
void initState() {
super.initState();
List<GetMenuEntriesList> menuData = Provider.of<DashboardProviderModel>(context, listen: false).getMenuEntriesList!;
List<GetMenuEntriesList> arr = menuData.where((GetMenuEntriesList e) => e.requestType == 'CONTACT').toList();
if (arr.isNotEmpty) {
menuEntries = arr[0];
} else {
menuEntries = new GetMenuEntriesList();
}
getEmployeeContacts();
}
@ -47,19 +59,7 @@ class _FamilyMembersState extends State<FamilyMembers> {
}
}
GetMenuEntriesList _getContactMenuEntries(BuildContext context) {
List<GetMenuEntriesList> menuData = context.watch<DashboardProviderModel>().getMenuEntriesList ?? <GetMenuEntriesList>[];
List<GetMenuEntriesList> arr = menuData.where((GetMenuEntriesList e) => e.requestType == 'CONTACT').toList();
return arr.isNotEmpty ? arr.first : GetMenuEntriesList();
}
@override
Widget build(BuildContext context) {
GetMenuEntriesList menuEntries = _getContactMenuEntries(context);
bool canAddFamilyMember = menuEntries.addButton == 'Y';
bool canUpdateFamilyMember = menuEntries.updateButton == 'Y';
bool canDeleteFamilyMember = menuEntries.deleteButton == 'Y';
return Scaffold(
appBar: AppBarWidget(
context,
@ -95,18 +95,16 @@ class _FamilyMembersState extends State<FamilyMembers> {
child: Icon(
Icons.edit,
size: 14,
color: canUpdateFamilyMember ? MyColors.grey67Color : MyColors.lightGreyColor,
color: menuEntries.updateButton == 'Y' ? MyColors.grey67Color : MyColors.lightGreyColor,
)),
TextSpan(
text: LocaleKeys.update.tr(),
style: TextStyle(
color: canUpdateFamilyMember ? MyColors.grey67Color : MyColors.lightGreyColor, fontSize: 12, letterSpacing: -0.36, fontWeight: FontWeight.w600),
color: menuEntries.updateButton == 'Y' ? MyColors.grey67Color : MyColors.lightGreyColor, fontSize: 12, letterSpacing: -0.36, fontWeight: FontWeight.w600),
recognizer: TapGestureRecognizer()
..onTap = () async {
relationId = getEmployeeContactsList[index].cONTACTRELATIONSHIPID;
if (canUpdateFamilyMember && relationId != null) {
showUpdateAlertDialog(context, relationId, 2, "UPDATE");
}
relationId = getEmployeeContactsList[index]!.cONTACTRELATIONSHIPID;
menuEntries.updateButton == 'Y' ? showUpdateAlertDialog(context, relationId, 2, "UPDATE") : null;
})
],
),
@ -119,21 +117,19 @@ class _FamilyMembersState extends State<FamilyMembers> {
child: Icon(
Icons.delete,
size: 15,
color: canDeleteFamilyMember ? MyColors.redColor : MyColors.lightGreyColor,
color: menuEntries.updateButton == 'Y' ? MyColors.redColor : MyColors.lightGreyColor,
),
),
TextSpan(
text: LocaleKeys.remove.tr(),
style: TextStyle(
color: canDeleteFamilyMember ? MyColors.redColor : MyColors.lightGreyColor, fontSize: 12, letterSpacing: -0.36, fontWeight: FontWeight.w600),
color: menuEntries.updateButton == 'Y' ? MyColors.redColor : MyColors.lightGreyColor, fontSize: 12, letterSpacing: -0.36, fontWeight: FontWeight.w600),
),
],
),
).onPress(() {
relationId = getEmployeeContactsList[index].cONTACTRELATIONSHIPID;
if (canDeleteFamilyMember && relationId != null) {
showRemoveAlertDialog(context, relationId);
}
relationId = getEmployeeContactsList[index]!.cONTACTRELATIONSHIPID;
menuEntries.updateButton == 'Y' ? showRemoveAlertDialog(context, relationId) : null;
}),
],
),
@ -143,13 +139,10 @@ class _FamilyMembersState extends State<FamilyMembers> {
),
DefaultButton(
LocaleKeys.addNewFamilyMember.tr(),
canAddFamilyMember
menuEntries.updateButton == 'Y'
? () async {
Navigator.pushNamed(
context,
AppRoutes.addUpdateFamilyMember,
arguments: FamilyMemberScreenParams(null, flag: 1, actionType: "ADD"),
);
Navigator.pushNamed(context, AppRoutes.addUpdateFamilyMember, arguments: FamilyMemberScreenParams( relationId, flag: 1, actionType: "ADD"));
// ProfileScreen();
}
: null)
.insideContainer,
@ -171,7 +164,6 @@ class _FamilyMembersState extends State<FamilyMembers> {
LocaleKeys.ok.tr(),
),
onPressed: () {
Navigator.pop(context);
Navigator.pushNamed(
context,
AppRoutes.addUpdateFamilyMember,
@ -211,7 +203,7 @@ class _FamilyMembersState extends State<FamilyMembers> {
LocaleKeys.ok.tr(),
),
onPressed: () {
Navigator.pushNamed(context, AppRoutes.deleteFamilyMember, arguments: relationId);
Navigator.pushNamed(context, AppRoutes.deleteFamilyMember, arguments: FamilyMemberScreenParams(relationId));
// continueDynamicForms();
},
);

@ -2,7 +2,8 @@ import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
// import 'package:flutter_html/flutter_html.dart';
import 'package:mohem_flutter_app/api/pending_transactions_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
@ -59,8 +60,8 @@ class _AnnouncementDetailsState extends State<AnnouncementDetails> {
),
),
),
Html(
data: AppState().isArabic(context) ? getAnnouncementDetailsObj?.bodyAR : getAnnouncementDetailsObj?.bodyEN,
HtmlWidget(
AppState().isArabic(context) ? getAnnouncementDetailsObj?.bodyAR??"" : getAnnouncementDetailsObj?.bodyEN ??"",
).paddingOnly(top: 12),
],
).objectContainerView().paddingAll(21),

@ -2,7 +2,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/api/profile_api_client.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
@ -13,64 +12,47 @@ import 'package:mohem_flutter_app/models/get_employee_contacts.model.dart';
import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
class ChildEducationAssistance extends StatefulWidget {
const ChildEducationAssistance({Key? key}) : super(key: key);
class ChildEducationAssistance extends StatelessWidget {
ChildEducationAssistance({Key? key}) : super(key: key);
@override
State<ChildEducationAssistance> createState() => _ChildEducationAssistanceState();
}
class _ChildEducationAssistanceState extends State<ChildEducationAssistance> {
GetMenuEntriesList? menuEntry;
List<GetEmployeeContactsList> empContactList = [];
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
menuEntry = ModalRoute.of(context)!.settings.arguments as GetMenuEntriesList;
getEmployeeContacts();
});
}
@override
Widget build(BuildContext context) {
menuEntry ??= ModalRoute.of(context)!.settings.arguments as GetMenuEntriesList;
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBarWidget(context, title: menuEntry?.prompt ?? ""),
body: empContactList.isNotEmpty
? ListView.separated(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.all(21),
separatorBuilder: (BuildContext cxt, int index) => 12.height,
itemCount: empContactList.length,
itemBuilder: (BuildContext context, int index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
"${empContactList[index].cONTACTNAME}".toText16(color: MyColors.grey3AColor),
"${LocaleKeys.dateOfBirth.tr()}: ${empContactList[index].dATEOFBIRTH?.split(" ").first ?? ""}".toText14(color: MyColors.grey3AColor, weight: FontWeight.w400),
"${empContactList[index].rELATIONSHIP}".toText11(color: MyColors.textMixColor),
],
).objectContainerView(disablePadding: false, center: false).onPress(() {
Navigator.pushNamed(context, AppRoutes.dynamicScreen,
arguments: DynamicListViewParams(empContactList[index].cONTACTNAME!, menuEntry!.functionName!, pContactRelationshipId: empContactList[index].cONTACTRELATIONSHIPID));
appBar: AppBarWidget(context, title: menuEntry!.prompt!),
body: FutureBuilder(
future: ProfileApiClient().getEmployeeContacts(),
builder: (BuildContext cxt, AsyncSnapshot<List<GetEmployeeContactsList>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasData) {
List<GetEmployeeContactsList> empContactList = snapshot.data!;
return ListView.separated(
padding: const EdgeInsets.all(21),
separatorBuilder: (BuildContext cxt, int index) => 12.height,
itemCount: empContactList.length,
itemBuilder: (BuildContext context, int index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
"${empContactList[index].cONTACTNAME}".toText16(color: MyColors.grey3AColor),
"${LocaleKeys.dateOfBirth.tr()}: ${empContactList[index].dATEOFBIRTH?.split(" ").first ?? ""}".toText14(color: MyColors.grey3AColor, weight: FontWeight.w400),
"${empContactList[index].rELATIONSHIP}".toText11(color: MyColors.textMixColor),
],
).objectContainerView(disablePadding: false, center: false).onPress(() {
Navigator.pushNamed(context, AppRoutes.dynamicScreen,
arguments: DynamicListViewParams(empContactList[index].cONTACTNAME!, menuEntry!.functionName!, pContactRelationshipId: empContactList[index].cONTACTRELATIONSHIPID));
});
});
})
: Utils.getNoDataWidget(context),
} else {
return LocaleKeys.noDataAvailable.tr().toText16().center;
}
},
),
);
}
void getEmployeeContacts() async {
try {
Utils.showLoading(context);
empContactList = await ProfileApiClient().getEmployeeContacts();
Utils.hideLoading(context);
setState(() {});
} catch (ex) {
Utils.hideLoading(context);
Utils.handleException(ex, context, null);
}
}
}

@ -1,333 +0,0 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/inAppWebView.dart';
import 'package:mohem_flutter_app/classes/utils.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/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/dashboard/courses_response_model.dart';
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/theme/colors.dart' as theme_colors;
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/sso_webview_widget.dart';
import 'package:provider/provider.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
class CoursesScreen extends StatefulWidget {
const CoursesScreen({Key? key}) : super(key: key);
@override
_CoursesScreenState createState() => _CoursesScreenState();
}
class _CoursesScreenState extends State<CoursesScreen> {
late DashboardProviderModel data;
final RefreshController _refreshController = RefreshController(initialRefresh: false);
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
data = Provider.of<DashboardProviderModel>(context, listen: false);
_onRefresh();
});
}
void _onRefresh() async {
await data.fetchCourses(context);
_refreshController.refreshCompleted();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBarWidget(
context,
title: LocaleKeys.courses.tr(),
),
body: Consumer<DashboardProviderModel>(
builder: (context, provider, child) {
return SmartRefresher(
enablePullDown: true,
enablePullUp: false,
controller: _refreshController,
onRefresh: _onRefresh,
child: provider.isCoursesLoading
? _buildShimmerLoading()
: provider.coursesList.isEmpty
? _buildEmptyState()
: _buildCoursesList(provider.coursesList),
);
},
),
);
}
Widget _buildShimmerLoading() {
return ListView.builder(
padding: const EdgeInsets.all(21),
itemCount: 5,
itemBuilder: (context, index) {
return Container(
margin: const EdgeInsets.only(bottom: 12),
height: 150,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: const Color(0xff000000).withOpacity(.05),
blurRadius: 26,
offset: const Offset(0, -3),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 20,
width: 200,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(4),
),
).toShimmer(),
12.height,
Container(
height: 14,
width: 100,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(4),
),
).toShimmer(),
const Spacer(),
Container(
height: 40,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(6),
),
).toShimmer(),
],
).paddingAll(14),
);
},
);
}
Widget _buildEmptyState() {
return Utils.getNoDataWidget(context);
}
Widget _buildCoursesList(List<CourseStatus> courses) {
return ListView.separated(
physics: const BouncingScrollPhysics(),
shrinkWrap: true,
padding: const EdgeInsets.all(21),
itemBuilder: (BuildContext context, int index) {
return _buildCourseCard(courses[index]);
},
separatorBuilder: (BuildContext context, int index) => 12.height,
itemCount: courses.length,
);
}
Widget _buildCourseCard(CourseStatus course) {
bool isArabic = AppState().isArabic(context);
String courseName = isArabic && course.cNameAr != null && course.cNameAr!.isNotEmpty
? course.cNameAr!
: course.cFullname ?? '';
String status = course.cStatus ?? 'Unknown';
Color statusColor = _getStatusColor(status);
return InkWell(
onTap: () => _openCourse(course.cUrl, course.token),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Course Title
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: MyColors.lightGreyEFColor,
borderRadius: BorderRadius.circular(8),
),
child: const Icon(
Icons.school_rounded,
color: MyColors.gradiantStartColor,
size: 20,
),
),
12.width,
Expanded(
child: courseName.toText14(isBold: true, color: MyColors.darkTextColor),
),
],
),
12.height,
// Status Badge
Row(
children: [
Icon(
_getStatusIcon(status),
size: 16,
color: statusColor,
),
6.width,
LocaleKeys.status.tr().toText11(color: MyColors.grey98Color),
8.width,
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: statusColor.withOpacity(0.3),
width: 1,
),
),
child: status.toText10(color: statusColor, isBold: true),
),
],
),
10.height,
// Start Date
if (course.cEnddate != null && course.cEnddate!.isNotEmpty)
Row(
children: [
const Icon(
Icons.calendar_month_rounded,
size: 16,
color: MyColors.darkIconColor,
),
6.width,
LocaleKeys.endDateT.tr().toText11(color: MyColors.grey98Color),
8.width,
Expanded(
child: _formatDate(course.cEnddate!).toText11(
color: MyColors.darkTextColor,
isBold: true,
),
),
],
),
if (course.cStartdate != null && course.cStartdate!.isNotEmpty) 14.height,
// Open Button
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
LocaleKeys.open.tr().toText12(color: MyColors.gradiantStartColor, isUnderLine: true),
6.width,
RotatedBox(
quarterTurns: AppState().isArabic(context) ? 2 : 4,
child: SvgPicture.asset(
"assets/images/arrow_next.svg",
color: MyColors.gradiantStartColor,
width: 16,
height: 16,
),
),
],
),
],
).objectContainerView(),
);
}
IconData _getStatusIcon(String status) {
if (status.toLowerCase().contains('passed')) {
return Icons.check_circle_rounded;
} else if (status.toLowerCase().contains('not passed') || status.toLowerCase().contains('failed')) {
return Icons.cancel_rounded;
} else if (status.toLowerCase().contains('in progress')) {
return Icons.pending_rounded;
} else {
return Icons.info_rounded;
}
}
Color _getStatusColor(String status) {
if (status.toLowerCase().contains('passed') && !status.toLowerCase().contains('not passed')) {
return Colors.green;
} else if (status.toLowerCase().contains('not passed') || status.toLowerCase().contains('failed')) {
return Colors.red;
} else if (status.toLowerCase().contains('in progress')) {
return Colors.yellow[700]!;
} else {
return MyColors.grey98Color;
}
}
DateTime? _parseDate(String? date) {
if (date == null || date.isEmpty) return null;
try {
return DateTime.parse(date.replaceAll(' ', 'T'));
} catch (e) {
try {
DateFormat format = DateFormat('yyyy-MM-dd HH:mm:ss');
return format.parse(date);
} catch (e) {
return null;
}
}
}
String _formatDate(String date) {
DateTime? dateTime = _parseDate(date);
if (dateTime == null) return date;
bool isArabic = AppState().isArabic(context);
DateFormat format = DateFormat('dd MMM yyyy, hh:mm a', isArabic ? 'ar' : 'en');
return format.format(dateTime);
}
void _openCourse(String? url, String? jwtToken) {
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => SsoLoginWebView(url: url, jwtToken: jwtToken, ssoType: SsoType.forCourses)));
//Utils.showToast(LocaleKeys.courseUrlNotAvailable.tr());
// return;
// }
//
// MyInAppBrowser browser = MyInAppBrowser(
// onExitCallback: () {},
// onLoadStartCallback: (String url) {},
// );
//
// var settings = InAppBrowserClassSettings(
// webViewSettings: InAppWebViewSettings(
// useShouldOverrideUrlLoading: false,
// transparentBackground: false,
// ),
// browserSettings: InAppBrowserSettings(
// hideUrlBar: true,
// hideTitleBar: false,
// toolbarTopBackgroundColor: theme_colors.primaryColor,
// ),
// );
//
// browser.openUrlRequest(
// urlRequest: URLRequest(url: WebUri(url)),
// settings: settings,
// );
}
@override
void dispose() {
_refreshController.dispose();
super.dispose();
}
}

@ -150,9 +150,7 @@ class _MowadhafhiHRRequestState extends State<MowadhafhiHRRequest> {
isReadOnly: false,
onChange: (String value) {
debugPrint(value);
setState(() {
description = value;
});
description = value;
},
),
],
@ -173,7 +171,7 @@ class _MowadhafhiHRRequestState extends State<MowadhafhiHRRequest> {
}
bool checkValidation() {
if (selectedServiceType == null || selectedDepartment == null || selectedSection == null || selectedTopic == null || description == "") {
if (selectedServiceType == null || selectedDepartment == null || selectedSection == null || selectedTopic == null) {
return false;
} else {
return true;
@ -192,78 +190,10 @@ class _MowadhafhiHRRequestState extends State<MowadhafhiHRRequest> {
title.toText16().expanded,
6.width,
SimpleButton(LocaleKeys.add.tr(), () async {
FilePickerResult? result = await FilePicker.platform.pickFiles(
allowMultiple: true,
);
List<PlatformFile>? result = await FilePicker.pickFiles();
if (result != null) {
// Maximum file size: 2 MB (in bytes)
const int maxFileSizeInBytes = 2 * 1024 * 1024; // 2 MB = 2097152 bytes
List<File> newFiles = [];
List<String> oversizedFiles = [];
for (PlatformFile platformFile in result.files) {
if (platformFile.path != null) {
File file = File(platformFile.path!);
// Get file size - check multiple sources for iOS compatibility
int fileSize = 0;
// Method 1: Check platformFile.size first
if (platformFile.size > 0) {
fileSize = platformFile.size;
debugPrint('Using platformFile.size: $fileSize bytes');
}
// Method 2: Always verify with file.length() as well
try {
int fileLengthSize = await file.length();
debugPrint('Using file.length(): $fileLengthSize bytes');
// Use the maximum of both to ensure we catch the real size
if (fileLengthSize > fileSize) {
fileSize = fileLengthSize;
debugPrint('Using larger value from file.length()');
}
} catch (e) {
debugPrint('Error getting file.length(): $e');
// If file.length() fails, rely on platformFile.size
if (fileSize == 0) {
debugPrint('Cannot determine file size, rejecting file: ${platformFile.name}');
oversizedFiles.add('${platformFile.name} (unknown size)');
continue;
}
}
double fileSizeMB = fileSize / (1024 * 1024);
debugPrint('Final file size - ${platformFile.name}: $fileSize bytes (${fileSizeMB.toStringAsFixed(2)} MB)');
// STRICT validation: Only accept files that are 2 MB or less
if (fileSize > 0 && fileSize <= maxFileSizeInBytes) {
debugPrint('✓ File accepted: ${platformFile.name}');
newFiles.add(file);
} else if (fileSize > maxFileSizeInBytes) {
debugPrint('✗ File REJECTED (too large): ${platformFile.name} - ${fileSizeMB.toStringAsFixed(2)} MB');
oversizedFiles.add('${platformFile.name} (${fileSizeMB.toStringAsFixed(2)} MB)');
} else {
debugPrint('✗ File REJECTED (invalid size): ${platformFile.name}');
oversizedFiles.add('${platformFile.name} (invalid)');
}
}
}
// Only add valid files
if (newFiles.isNotEmpty) {
attachmentFiles = attachmentFiles + newFiles;
attachmentFiles = attachmentFiles.toSet().toList();
debugPrint('Total files attached: ${attachmentFiles.length}');
}
// Show error message for rejected files
if (oversizedFiles.isNotEmpty) {
String fileList = oversizedFiles.join('\n');
Utils.showToast('Max 2MB File allowed.');
debugPrint('Total rejected files: ${oversizedFiles.length}');
}
attachmentFiles = attachmentFiles + result.map((path) => File(path.uri.toFilePath())).toList();
attachmentFiles = attachmentFiles.toSet().toList();
setState(() {});
}
}, fontSize: 14),

@ -92,17 +92,22 @@ class _MyDocumentDetailScreenState extends State<MyDocumentDetailScreen> {
),
12.height,
DottedBorder(
borderType: BorderType.RRect,
radius: const Radius.circular(10),
padding: const EdgeInsets.all(12),
dashPattern: const <double>[2, 1],
color: MyColors.selectedBorderColor,
options: RoundedRectDottedBorderOptions(
radius: const Radius.circular(10),
padding: const EdgeInsets.all(12),
dashPattern: const <double>[2, 1],
color: MyColors.selectedBorderColor,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Transform.rotate(
angle: 45,
child: const Icon(Icons.attach_file_rounded, size: 16, color: MyColors.selectedBorderColor),
child: const Icon(
Icons.attach_file_rounded,
size: 16,
color: MyColors.selectedBorderColor,
),
),
4.width,
"Attach Image".toText14(color: MyColors.selectedBorderColor),

@ -67,10 +67,10 @@ class _MyRequestsState extends State<MyRequests> {
isReadOnly: false,
).paddingOnly(bottom: 12),
itemBuilder: (_) => <PopupMenuItem<int>>[
for (int i = 0; i < getConcurrentProgramsList!.length; i++) PopupMenuItem<int>(child: Text(getConcurrentProgramsList![i].uSERCONCURRENTPROGRAMNAME!), value: i),
for (int i = 0; i < getConcurrentProgramsList.length; i++) PopupMenuItem<int>(child: Text(getConcurrentProgramsList[i].uSERCONCURRENTPROGRAMNAME!), value: i),
],
onSelected: (int popupIndex) {
selectedConcurrentProgramList = getConcurrentProgramsList![popupIndex];
selectedConcurrentProgramList = getConcurrentProgramsList[popupIndex];
getCCPTransactions(selectedConcurrentProgramList?.cONCURRENTPROGRAMNAME);
setState(() {});
}),
@ -105,7 +105,7 @@ class _MyRequestsState extends State<MyRequests> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
("Request ID: " + getCCPTransactionsList[index].rEQUESTID.toString()).toText12(color: MyColors.grey57Color),
DateUtil.formatDateToDate(DateUtil.convertStringToDate(getCCPTransactionsList[index].rEQUESTDATE!), false).toText12(color: MyColors.grey70Color),
DateUtil.formatDateToDate(DateUtil.convertStringToDate(getCCPTransactionsList[index].rEQUESTDATE), false).toText12(color: MyColors.grey70Color),
],
),
Container(

@ -6,8 +6,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
@ -16,7 +16,7 @@ import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';
// import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher.dart';
class OffersAndDiscountsDetails extends StatefulWidget {
@ -68,10 +68,12 @@ class _OffersAndDiscountsDetailsState extends State<OffersAndDiscountsDetails> {
AppState().isArabic(context)
? getOffersList[0].titleAr!.toText22(isBold: true, color: const Color(0xff2B353E)).center
: getOffersList[0].titleEn!.toText22(isBold: true, color: const Color(0xff2B353E)).center,
Html(
data: AppState().isArabic(context) ? getOffersList[0].descriptionAr! : getOffersList[0].descriptionEn ?? "",
onLinkTap: (String? url, Map<String, String> attributes, _) {
HtmlWidget(
AppState().isArabic(context) ? getOffersList[0].descriptionAr! : getOffersList[0].descriptionEn ?? "",
onTapUrl: (String? url) {
launchUrl(Uri.parse(url!));
return true;
},
),
checkDate(getOffersList[0].endDate!).paddingOnly(left: 8),
@ -122,7 +124,7 @@ class _OffersAndDiscountsDetailsState extends State<OffersAndDiscountsDetails> {
Directory tempDir = await getTemporaryDirectory();
File file = await File('${tempDir.path}/${DateTime.now().toString()}.png').create();
await file.writeAsBytes(pngBytes);
await SharePlus.instance.share(ShareParams(files: [(XFile(file.path))], text: AppState().isArabic(context) ? getOffersList[0].titleAr : getOffersList[0].titleEn));
// await SharePlus.instance.share(ShareParams(files: [(XFile(file.path))], text: AppState().isArabic(context) ? getOffersList[0].titleAr : getOffersList[0].titleEn));
} catch (ex) {
debugPrint(ex.toString());
}

@ -5,17 +5,14 @@ import 'package:flutter/material.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/date_uitl.dart';
import 'package:mohem_flutter_app/classes/file_process.dart';
import 'package:mohem_flutter_app/classes/utils.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/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/dashboard/get_accural_ticket_balance_model.dart';
import 'package:mohem_flutter_app/models/eit/get_eit_transaction_model.dart';
import 'package:mohem_flutter_app/models/eit_attachment_ticket_model.dart';
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/balances_dashboard_widget.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:mohem_flutter_app/widgets/item_detail_view_widget.dart';
import 'package:mohem_flutter_app/widgets/sso_webview_widget.dart';
@ -58,27 +55,26 @@ class _TicketDetailedScreenState extends State<TicketDetailedScreen> {
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBarWidget(context, title: LocaleKeys.ticketDetails.tr()),
appBar: AppBarWidget(context, title: "Ticket Details"),
body: Column(
children: <Widget>[
Expanded(
child: ListView(
padding: const EdgeInsets.all(21),
physics: const BouncingScrollPhysics(),
children: <Widget>[
if (dashboardProviderModel == null && dashboardProviderModel?.accrualTicketBalanceList == null) ...<Widget>[Utils.getNoDataWidget(context).paddingOnly(top: 50)] else ...<Widget>[
Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.0),
boxShadow: <BoxShadow>[BoxShadow(color: Colors.grey.withOpacity(0.2), spreadRadius: 2, blurRadius: 5, offset: const Offset(0, 3))],
),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(child: LocaleKeys.currentTicketBalance.tr().toText20()),
ListView(
padding: const EdgeInsets.all(21),
physics: const BouncingScrollPhysics(),
children: <Widget>[
if (dashboardProviderModel == null && dashboardProviderModel?.accrualTicketBalanceList == null) ...<Widget>[Utils.getNoDataWidget(context).paddingOnly(top: 50)] else ...<Widget>[
Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.0),
boxShadow: <BoxShadow>[BoxShadow(color: Colors.grey.withOpacity(0.2), spreadRadius: 2, blurRadius: 5, offset: const Offset(0, 3))],
),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
"Current Ticket Balance".toText20().expanded,
Row(
children: <Widget>[
const Icon(Icons.calendar_month_rounded, color: MyColors.darkIconColor, size: 16),
@ -116,19 +112,21 @@ class _TicketDetailedScreenState extends State<TicketDetailedScreen> {
),
),
const SizedBox(height: 21),
(dashboardProviderModel != null && dashboardProviderModel!.ticketHistoryTransactionList != null && dashboardProviderModel!.ticketHistoryTransactionList!.isNotEmpty)
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
dashboardProviderModel == null && dashboardProviderModel!.ticketHistoryTransactionList == null
? const SizedBox()
: ListView(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: <Widget>[
LocaleKeys.ticketHistory.tr().toText20(),
"Tickets History".toText20().expanded,
12.height,
ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.zero,
itemBuilder: (BuildContext cxt, int cardIndex) {
List<CollectionTransaction> transactionDetails = dashboardProviderModel!.ticketHistoryTransactionList![cardIndex];
const Set<String> allowedSegmentNames = <String>{
var transactionDetails = dashboardProviderModel!.ticketHistoryTransactionList![cardIndex];
const allowedSegmentNames = <String>{
"TICKETS_ROUTE",
"TRAVELER_NAME",
"TICKETS_EFFECTIVE_DATE",
@ -137,22 +135,11 @@ class _TicketDetailedScreenState extends State<TicketDetailedScreen> {
"TICKET_COMP_SHARE",
"TICKET_EMP_SHARE",
};
// Get items for display
List<CollectionTransaction> uniqueDetails =
var uniqueDetails =
transactionDetails
.where(
(CollectionTransaction item) =>
item.dISPLAYFLAG != 'N' && item.sEGMENTPROMPT != null && item.sEGMENTPROMPT!.isNotEmpty && allowedSegmentNames.contains(item.sEGMENTNAME),
)
.where((item) => item.dISPLAYFLAG != 'N' && item.sEGMENTPROMPT != null && item.sEGMENTPROMPT!.isNotEmpty && allowedSegmentNames.contains(item.sEGMENTNAME))
.toList();
// Get PEI_EXTRA_INFO_ID value for View PDF
CollectionTransaction? peiExtraInfoItem = transactionDetails.firstWhere(
(CollectionTransaction item) => item.aPPLICATIONCOLUMNNAME == "PEI_EXTRA_INFO_ID",
orElse: () => CollectionTransaction(),
);
num? peiExtraInfoId = peiExtraInfoItem.nUMBERVALUE;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
@ -161,15 +148,15 @@ class _TicketDetailedScreenState extends State<TicketDetailedScreen> {
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: (uniqueDetails.length / 2).ceil(),
itemBuilder: (BuildContext context, int rowIndex) {
itemBuilder: (context, rowIndex) {
int firstIndex = rowIndex * 2;
int? secondIndex = (firstIndex + 1 < uniqueDetails.length) ? firstIndex + 1 : null;
bool isLastRow = (rowIndex == (uniqueDetails.length / 2).ceil() - 1);
CollectionTransaction item1 = uniqueDetails[firstIndex];
ItemDetailViewCol child1 = ItemDetailViewCol(item1.sEGMENTPROMPT!, item1.vARCHAR2VALUE ?? item1.nUMBERVALUE?.toString() ?? "");
var item1 = uniqueDetails[firstIndex];
var child1 = ItemDetailViewCol(item1.sEGMENTPROMPT!, item1.vARCHAR2VALUE ?? item1.nUMBERVALUE?.toString() ?? "");
Widget child2;
if (secondIndex != null) {
CollectionTransaction item2 = uniqueDetails[secondIndex];
var item2 = uniqueDetails[secondIndex];
child2 = ItemDetailViewCol(item2.sEGMENTPROMPT!, item2.vARCHAR2VALUE ?? item2.nUMBERVALUE?.toString() ?? "");
} else {
child2 = const SizedBox(); // Empty widget if there is no second item
@ -177,40 +164,6 @@ class _TicketDetailedScreenState extends State<TicketDetailedScreen> {
return ItemDetailGrid(child1, child2, isItLast: isLastRow);
},
),
// View PDF link at the bottom
if (peiExtraInfoId != null) ...[
const SizedBox(height: 5),
Align(
alignment: Alignment.centerRight,
child: GestureDetector(
onTap: () async {
Utils.showLoading(context);
try {
GetEitAttachmentTicket? result = await dashboardProviderModel!.getTicketEitPDFDownload(peiExtraInfoId: peiExtraInfoId.toString());
if (result != null && result.getEitAttachmentList != null && result.getEitAttachmentList!.isNotEmpty) {
String? base64Data = result.getEitAttachmentList!.first.fileData;
if (base64Data != null && base64Data.isNotEmpty) {
String fileName = result.getEitAttachmentList!.first.fileName!;
await FileProcess.downloadFile(base64Data, fileName);
Utils.hideLoading(context);
FileProcess.openFile(fileName);
} else {
Utils.hideLoading(context);
Utils.showToast("PDF data not available");
}
} else {
Utils.hideLoading(context);
Utils.showToast("No attachment found");
}
} catch (e) {
Utils.hideLoading(context);
Utils.showToast("Something Went Wrong");
}
},
child: Padding(padding: const EdgeInsets.only(top: 8.0, left: 0.0, bottom: 8.0), child: "View PDF".toText14(isUnderLine: true, isBold: true)),
),
),
],
],
).objectContainerView();
},
@ -218,15 +171,13 @@ class _TicketDetailedScreenState extends State<TicketDetailedScreen> {
itemCount: dashboardProviderModel!.ticketHistoryTransactionList!.length,
),
],
)
: const SizedBox(),
],
),
],
),
),
DefaultButton(LocaleKeys.proceedBooking.tr(), () {
],
).expanded,
DefaultButton("Proceed For Booking", () {
if (widget.url != null && widget.jwtToken != null) {
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => SsoLoginWebView(url: widget.url, jwtToken: widget.jwtToken, ssoType: SsoType.forTicket)));
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => SsoLoginWebView(url: widget.url, jwtToken: widget.jwtToken)));
}
}).insideContainer,
],

@ -211,7 +211,7 @@ class _ItemHistoryScreenState extends State<ItemHistoryScreen> {
interval: 1,
getTitlesWidget: (double value, TitleMeta meta) {
return SideTitleWidget(
axisSide: meta.axisSide,
meta: meta,
child: Text(reversedList[int.parse(meta.formattedValue)].cREATIONDATE!, style: TextStyle(fontSize: 10)),
);
})),
@ -222,7 +222,7 @@ class _ItemHistoryScreenState extends State<ItemHistoryScreen> {
interval: 1,
getTitlesWidget: (double value, TitleMeta meta) {
return SideTitleWidget(
axisSide: meta.axisSide,
meta: meta,
child: Text(meta.formattedValue, style: TextStyle(fontSize: 10)),
);
})),
@ -233,7 +233,7 @@ class _ItemHistoryScreenState extends State<ItemHistoryScreen> {
reservedSize: 15,
getTitlesWidget: (double value, TitleMeta meta) {
return SideTitleWidget(
axisSide: meta.axisSide,
meta: meta,
child: Text("", style: TextStyle(fontSize: 10)),
);
})),

@ -21,11 +21,7 @@ class ApprovalLevelfragment extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (wFHistory.isNotEmpty) {
//TODO: This COde is Written For Future. If Needed Enable to Filter The Empty Action Requests.
// wFHistory = wFHistory.where((item) => item.action != null && item.action!.isNotEmpty).toList();
wFHistory = wFHistory.reversed.toList();
}
if (wFHistory.isNotEmpty) wFHistory = wFHistory.reversed.toList();
return Container(
width: double.infinity,
height: double.infinity,
@ -83,8 +79,7 @@ class ApprovalLevelfragment extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(history.name ?? history.approvalLevel ?? "").toText16(),
// history.name!.toText16(),
history.name!.toText16(),
if ((history.notes ?? "").isNotEmpty) history.notes!.toText12(color: MyColors.grey57Color),
4.height,
Row(
@ -169,8 +164,8 @@ class ApprovalLevelfragment extends StatelessWidget {
if (wFHistory[index].date!.isEmpty || wFHistory[index].action!.toLowerCase() == "NO ACTION" || wFHistory[index + 1].date! == "") {
return "";
} else {
DateTime dateTimeTo = DateUtil.convertSimpleStringDateToDate(wFHistory[index].date!, isITG: true);
DateTime dateTimeFrom = DateUtil.convertSimpleStringDateToDate(wFHistory[index + 1].date!, isITG: true);
DateTime dateTimeTo = DateUtil.convertSimpleStringDateToDate(wFHistory[index].date!);
DateTime dateTimeFrom = DateUtil.convertSimpleStringDateToDate(wFHistory[index + 1].date!);
Duration duration = dateTimeTo.difference(dateTimeFrom);
return "Action duration: " + DateUtil.formatDuration(duration);
}

@ -5,7 +5,6 @@ import 'package:flutter_svg/svg.dart';
import 'package:mohem_flutter_app/api/worklist/worklist_api_client.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/consts.dart';
import 'package:mohem_flutter_app/classes/date_uitl.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
@ -22,8 +21,6 @@ import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/worklist_advance_search_bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/dynamic_forms/dynamic_textfield_widget.dart';
import 'package:mohem_flutter_app/widgets/sso_webview_widget.dart';
import 'package:provider/provider.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
@ -110,13 +107,13 @@ class _WorkListScreenState extends State<WorkListScreen> {
),
WorkListItemTypeModelData(
value: 0,
name: 'ENSAT',
name: 'COC',
fullName: LocaleKeys.itemCreation.tr(),
active: false,
color: [const Color(0xff787299), Color(0xff1AB170)],
color: [Color(0xff787299), Color(0xff1AB170)],
icon: "assets/images/miss_swipe.svg",
key: 'COC',
disable: false,
disable: true,
),
WorkListItemTypeModelData(
value: 0,
@ -138,6 +135,7 @@ class _WorkListScreenState extends State<WorkListScreen> {
key: 'HMG_FA_D',
disable: false,
),
];
int? workListItemIndex;
@ -152,7 +150,6 @@ class _WorkListScreenState extends State<WorkListScreen> {
final ScrollController _controller = ScrollController();
int pNotificationType = 1;
String prPoFilterText = "";
ScrollController? _scrollController;
@override
@ -234,12 +231,6 @@ class _WorkListScreenState extends State<WorkListScreen> {
if ((itgFormsModel?.requestType?.length ?? 0) > 0) {
itgRequestTypeIndex = 0;
}
} else if (workListItemTypes[workListItemIndex!].key == "COC") {
String? enSAToken = await DashboardProviderModel().getENSATItgToken();
Utils.hideLoading(context);
dynamic url = "${ApiConsts.enSATLiveURL}?empId=${AppState().getUserName}&token=$enSAToken";
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => SsoLoginWebView(url: url, jwtToken: enSAToken, ssoType: SsoType.forEnsat)));
} else {
itgRequestTypeIndex = null;
List<WorkListResponseModel>? _list = await WorkListApiClient().getWorkList(pageNumber, workListItemTypes[workListItemIndex!].key, pNotificationType.toString());
@ -320,7 +311,6 @@ class _WorkListScreenState extends State<WorkListScreen> {
}
if (workListItemIndex != index && !workListItemTypes[index].disable) {
workListItemIndex = index;
prPoFilterText = "";
pageNumber = 1;
if (workListItemTypes[index].value == 0) {
workList = [];
@ -355,20 +345,6 @@ class _WorkListScreenState extends State<WorkListScreen> {
.paddingOnly(left: 21, right: 21),
],
),
if (_isPrOrPoSelected())
DynamicTextFieldWidget(
"",
_getPrPoFilterHint(),
isEnable: true,
isPopup: false,
lines: 1,
isInputTypeNum: false,
isReadOnly: false,
onChange: (String value) {
prPoFilterText = value;
setState(() {});
},
).paddingOnly(left: 21, right: 21, top: 10),
SmartRefresher(
enablePullDown: true,
enablePullUp: false,
@ -421,16 +397,16 @@ class _WorkListScreenState extends State<WorkListScreen> {
],
)
: workList != null
? ((_getDisplayedWorkList()).isEmpty
? ((workList!).isEmpty
? Utils.getNoDataWidget(context).paddingOnly(top: 100)
: ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return rowItem(workListItemTypes[workListItemIndex!], _getDisplayedWorkList()[index], index);
return rowItem(workListItemTypes[workListItemIndex!], workList![index], index);
},
separatorBuilder: (context, index) => 12.height,
itemCount: _getDisplayedWorkList().length,
itemCount: workList?.length ?? 0,
padding: const EdgeInsets.all(21),
))
: const SizedBox(),
@ -496,7 +472,7 @@ class _WorkListScreenState extends State<WorkListScreen> {
10.height,
Row(
children: [
DateUtil.formatDateToDate(DateUtil.convertStringToDate(requestDetails.modifiedDate!), false).toText10(color: MyColors.lightTextColor).expanded,
DateUtil.formatDateToDate(DateUtil.convertStringToDate(requestDetails.modifiedDate), false).toText10(color: MyColors.lightTextColor).expanded,
RotatedBox(quarterTurns: AppState().isArabic(context) ? 2 : 4, child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.darkIconColor)),
],
),
@ -524,7 +500,7 @@ class _WorkListScreenState extends State<WorkListScreen> {
}
}
Utils.showLoading(context);
List<WorkListResponseModel>? responseList = await WorkListApiClient().getWorkList(
workList = await WorkListApiClient().getWorkList(
pageNumber,
selectedItemTypeID,
selectedViewID.toString(),
@ -533,8 +509,6 @@ class _WorkListScreenState extends State<WorkListScreen> {
pSentDate: searchByDate,
pSearchItemType: searchByInput,
);
workList = responseList;
prPoFilterText = "";
workListItemIndex = index;
AppState().setWorkList = workList;
Utils.hideLoading(context);
@ -544,35 +518,11 @@ class _WorkListScreenState extends State<WorkListScreen> {
);
}
bool _isPrOrPoSelected() {
if (workListItemIndex == null) return false;
String key = workListItemTypes[workListItemIndex!].key ?? "";
return key == "REQAPPRV" || key == "POAPPRV";
}
List<WorkListResponseModel> _getDisplayedWorkList() {
List<WorkListResponseModel> source = workList ?? [];
if (!_isPrOrPoSelected()) return source;
String query = prPoFilterText.trim().toLowerCase();
if (query.isEmpty) return source;
return source.where((item) => (item.sUBJECT ?? "").toLowerCase().contains(query)).toList();
}
String _getPrPoFilterHint() {
if (workListItemIndex == null) return "filterEnterPrDetailsHere".tr();
String key = workListItemTypes[workListItemIndex!].key;
if (key == "POAPPRV") {
return "filterEnterPoDetailsHere".tr();
}
return "filterEnterPrDetailsHere".tr();
}
Widget rowItem(WorkListItemTypeModelData data, WorkListResponseModel workData, int index) {
return InkWell(
onTap: () async {
AppState().setWorkListIndex = index;
var data = workData;
var data = workList![index];
var shouldReloadData = await Navigator.pushNamed(context, AppRoutes.workListDetail, arguments: workData);
if (shouldReloadData != null) {
if (shouldReloadData.toString() == "delegate_reload") {
@ -585,7 +535,7 @@ class _WorkListScreenState extends State<WorkListScreen> {
setState(() {});
}
} else {
workList!.removeWhere((item) => item.nOTIFICATIONID == data.nOTIFICATIONID);
workList!.remove(data);
AppState().setWorkList = workList;
if (data.iTEMTYPE == "ITG") {
} else {

@ -602,7 +602,6 @@ class InfoFragment extends StatelessWidget {
ItemDetailViewCol(
LocaleKeys.responder.tr(), workListData!.rESPONDER ?? ""),
),
Column(
children: getPRHeaderValues(),
)
@ -751,7 +750,7 @@ class InfoFragment extends StatelessWidget {
ItemDetailGrid(
ItemDetailViewCol( LocaleKeys.requestDate.tr(),
DateUtil.formatDateToDate(
DateUtil.convertStringToDate(data.paymentDetailsList![0].requestDate!), false) ?? ""),
DateUtil.convertStringToDate(data.paymentDetailsList![0].requestDate), false) ?? ""),
ItemDetailViewCol(
LocaleKeys.requesterEmpName.tr(), data.paymentDetailsList![0].requesterEmployeeName ?? ""),
),

@ -13,8 +13,7 @@ import 'package:mohem_flutter_app/models/get_item_creation_ntf_body_list_model.d
import 'package:mohem_flutter_app/models/get_mo_notification_body_list_model.dart';
import 'package:mohem_flutter_app/models/get_po_notification_body_list_model.dart';
import 'package:mohem_flutter_app/models/get_pr_information_list.dart';
import 'package:mohem_flutter_app/models/get_pr_notification_body_list_model.dart'
as get_pr_notification_body_list_model;
import 'package:mohem_flutter_app/models/get_pr_notification_body_list_model.dart' as get_pr_notification_body_list_model;
import 'package:mohem_flutter_app/ui/work_list/item_history_screen.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:mohem_flutter_app/widgets/item_detail_view_widget.dart';
@ -52,465 +51,250 @@ class RequestFragment extends StatelessWidget {
Widget poLinesDataView(BuildContext context) {
return ExpandableNotifier(
child: ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder:
(cxt, index) =>
ExpandablePanel(
header: poLinesList[index].iTEMDESCRIPTION!.toText14(),
collapsed: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.code.tr(),
poLinesList[index].iTEMCODE ?? "",
),
ItemDetailViewCol(
LocaleKeys.mfg.tr(),
poLinesList[index].mFG ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.lineType.tr(),
poLinesList[index].lINETYPE?.toString() ?? "",
),
ItemDetailViewCol(
LocaleKeys.unit.tr(),
poLinesList[index].uOM ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.price.tr(),
poLinesList[index].uNITPRICE?.toString() ?? "",
),
ItemDetailViewCol(
LocaleKeys.lineAmount.tr(),
poLinesList[index].lINEAMOUNT?.toString() ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.quantity.tr(),
poLinesList[index].qUANTITY?.toString() ?? "",
),
ItemDetailViewCol(
LocaleKeys.lineDiscount.tr(),
poLinesList[index].lINEDISCPERCENTAGE?.toString() ??
"",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.needByDate.tr(),
poLinesList[index].nEEDBYDATE ?? "",
),
ItemDetailViewCol(
LocaleKeys.promisedDate.tr(),
poLinesList[index].pROMISEDDATE ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.deliverToLocation.tr(),
poLinesList[index].dELIVERTOLOCATION ?? "",
),
ItemDetailViewCol(
LocaleKeys.requisitionNumber.tr(),
poLinesList[index].pRNUM ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.requester.tr(),
poLinesList[index].rEQUESTOR ?? "",
),
Container(),
),
12.height,
Row(
children: [
DefaultButton(LocaleKeys.itemHistory.tr(), () {
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (cxt, index) => ExpandablePanel(
header: poLinesList[index].iTEMDESCRIPTION!.toText14(),
collapsed: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.code.tr(), poLinesList[index].iTEMCODE ?? ""),
ItemDetailViewCol(LocaleKeys.mfg.tr(), poLinesList[index].mFG ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.lineType.tr(), poLinesList[index].lINETYPE?.toString() ?? ""),
ItemDetailViewCol(LocaleKeys.unit.tr(), poLinesList[index].uOM ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.price.tr(), poLinesList[index].uNITPRICE?.toString() ?? ""),
ItemDetailViewCol(LocaleKeys.lineAmount.tr(), poLinesList[index].lINEAMOUNT?.toString() ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.quantity.tr(), poLinesList[index].qUANTITY?.toString() ?? ""),
ItemDetailViewCol(LocaleKeys.lineDiscount.tr(), poLinesList[index].lINEDISCPERCENTAGE?.toString() ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.needByDate.tr(), poLinesList[index].nEEDBYDATE ?? ""),
ItemDetailViewCol(LocaleKeys.promisedDate.tr(), poLinesList[index].pROMISEDDATE ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.deliverToLocation.tr(), poLinesList[index].dELIVERTOLOCATION ?? ""),
ItemDetailViewCol(LocaleKeys.requisitionNumber.tr(), poLinesList[index].pRNUM ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.requester.tr(), poLinesList[index].rEQUESTOR ?? ""),
Container(),
),
12.height,
Row(
children: [
DefaultButton(
LocaleKeys.itemHistory.tr(),
() {
Navigator.pushNamed(
cxt,
AppRoutes.itemHistory,
arguments: ItemHistoryScreenParams(
title: LocaleKeys.itemHistory.tr(),
isMO: false,
pItemId: poLinesList[index].iTEMID,
),
arguments: ItemHistoryScreenParams(title: LocaleKeys.itemHistory.tr(), isMO: false, pItemId: poLinesList[index].iTEMID),
);
}, fontSize: 13).expanded,
12.width,
DefaultButton(LocaleKeys.quotationAnalysis.tr(), () {
},
fontSize: 13,
).expanded,
12.width,
DefaultButton(
LocaleKeys.quotationAnalysis.tr(),
() {
Navigator.pushNamed(
cxt,
AppRoutes.itemHistory,
arguments: ItemHistoryScreenParams(
isItemHistory: false,
isMO: false,
title: LocaleKeys.quotationAnalysis.tr(),
pItemId: poLinesList[index].iTEMID,
pPoHeaderId: poLinesList[index].pOHEADERID,
),
);
}, fontSize: 13).expanded,
12.width,
DefaultButton("PR Details", () {
getPRDetails(
context,
poLinesList[index].pOLINEID.toString(),
isItemHistory: false, isMO: false, title: LocaleKeys.quotationAnalysis.tr(), pItemId: poLinesList[index].iTEMID, pPoHeaderId: poLinesList[index].pOHEADERID),
);
}, fontSize: 13).expanded,
],
),
],
),
expanded: const SizedBox(),
).objectContainerView(),
separatorBuilder: (cxt, index) => 12.height,
itemCount: poLinesList.length,
),
},
fontSize: 13,
).expanded,
12.width,
DefaultButton(
"PR Details",
() {
getPRDetails(context, poLinesList[index].pOLINEID.toString());
},
fontSize: 13,
).expanded,
],
)
],
),
expanded: const SizedBox(),
).objectContainerView(),
separatorBuilder: (cxt, index) => 12.height,
itemCount: poLinesList.length),
);
}
Widget prLinesDataView() {
return ExpandableNotifier(
child: ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder:
(cxt, index) =>
ExpandablePanel(
header: prLinesList[index].dESCRIPTION.toString().toText14(
color: MyColors.textMixColor,
),
collapsed: Column(
children: [
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.costCenter.tr(),
prLinesList[index].cOSTCENTER ?? "",
),
ItemDetailViewCol(
LocaleKeys.code.tr(),
prLinesList[index].iTEMCODE ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.unit.tr(),
prLinesList[index].uOM ?? "",
),
ItemDetailViewCol(
LocaleKeys.priceSAR.tr(),
prLinesList[index].uNITPRICE.toString() ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.amountSAR.tr(),
prLinesList[index].lINEAMOUNT.toString() ?? "",
),
ItemDetailViewCol(
LocaleKeys.quantity.tr(),
prLinesList[index].qUANTITY.toString() ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.destination_type.tr(),
prLinesList[index].dESTINATIONTYPE ?? "",
),
ItemDetailViewCol(
LocaleKeys.destination_organization_name.tr(),
prLinesList[index].dESTINATIONORGANIZATIONNAME ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.amuLastThreeMonths.tr(),
prLinesList[index].iTEMAMU.toString() ?? "",
),
Container(),
isItLast: true,
),
// ItemDetailGrid(
// ItemDetailViewCol(getPrNotificationBodyList.pRHeader![1].hDRATTRIBUTENAME!, getPrNotificationBodyList.pRHeader![1].hDRATTRIBUTEVALUE ?? ""),
// Container(),
// isItLast: true,
// ),
],
),
expanded: const SizedBox(),
).objectContainerView(),
separatorBuilder: (cxt, index) => 12.height,
itemCount: prLinesList.length,
),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (cxt, index) => ExpandablePanel(
header: prLinesList[index].dESCRIPTION.toString().toText14(color: MyColors.textMixColor),
collapsed: Column(
children: [
ItemDetailGrid(
ItemDetailViewCol("Cost Center", prLinesList[index].cOSTCENTER ?? ""),
ItemDetailViewCol("Code", prLinesList[index].iTEMCODE ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol("Unit", prLinesList[index].uOM ?? ""),
ItemDetailViewCol("Price (SAR)", prLinesList[index].uNITPRICE.toString() ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol("Amount (SAR)", prLinesList[index].lINEAMOUNT.toString() ?? ""),
ItemDetailViewCol("Quantity", prLinesList[index].qUANTITY.toString() ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol("AMU (Last 3 months)", prLinesList[index].iTEMAMU.toString() ?? ""),
Container(),
isItLast: true,
),
// ItemDetailGrid(
// ItemDetailViewCol(getPrNotificationBodyList.pRHeader![1].hDRATTRIBUTENAME!, getPrNotificationBodyList.pRHeader![1].hDRATTRIBUTEVALUE ?? ""),
// Container(),
// isItLast: true,
// ),
],
),
expanded: const SizedBox(),
).objectContainerView(),
separatorBuilder: (cxt, index) => 12.height,
itemCount: prLinesList.length),
);
}
Widget moNotificationDataView() {
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder:
(cxt, index) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.code.tr(),
moNotificationBodyList[index].iTEMCODE ?? "",
),
ItemDetailViewCol(
LocaleKeys.unit.tr(),
moNotificationBodyList[index].uOM ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.quantity.tr(),
moNotificationBodyList[index].qUANTITY?.toString() ?? "",
),
ItemDetailViewCol(
LocaleKeys.dateRequired.tr(),
moNotificationBodyList[index].dATEREQUIRED ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.lineStatus.tr(),
moNotificationBodyList[index].lINESTATUS ?? "",
),
ItemDetailViewCol(
LocaleKeys.statusDate.tr(),
moNotificationBodyList[index].sTATUSDATE ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.transactionType.tr(),
moNotificationBodyList[index].tRANSACTIONTYPENAME ?? "",
),
ItemDetailViewCol(
LocaleKeys.operatingUnit.tr(),
moNotificationBodyList[index].oPERATINGUNIT ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.organizationCode.tr(),
moNotificationBodyList[index].oRGANIZATIONCODE ?? "",
),
ItemDetailViewCol(
LocaleKeys.organization.tr(),
moNotificationBodyList[index].oRGANIZATIONNAME ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.fromSubInventory.tr(),
moNotificationBodyList[index].fROMSUBINVENTORY ?? "",
),
ItemDetailViewCol(
LocaleKeys.fromLocator.tr(),
moNotificationBodyList[index].fROMLOCATOR ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.toSubInventory.tr(),
moNotificationBodyList[index].tOSUBINVENTORY ?? "",
),
ItemDetailViewCol(
LocaleKeys.toLocator.tr(),
moNotificationBodyList[index].tOLOCATOR ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.shipToLocator.tr(),
moNotificationBodyList[index].sHIPTOLOCATION ?? "",
),
Container(),
),
12.height,
DefaultButton(LocaleKeys.itemHistory.tr(), () {
Navigator.pushNamed(
cxt,
AppRoutes.itemHistory,
arguments: ItemHistoryScreenParams(
title: LocaleKeys.itemHistory.tr(),
pItemId: moNotificationBodyList[index].iTEMID,
pOrgId: moNotificationBodyList[index].oRGID,
),
);
}),
],
).objectContainerView(
title: moNotificationBodyList[index].dESCRIPTION ?? "",
),
separatorBuilder: (cxt, index) => 12.height,
itemCount: moNotificationBodyList.length,
);
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (cxt, index) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.code.tr(), moNotificationBodyList[index].iTEMCODE ?? ""),
ItemDetailViewCol(LocaleKeys.unit.tr(), moNotificationBodyList[index].uOM ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.quantity.tr(), moNotificationBodyList[index].qUANTITY?.toString() ?? ""),
ItemDetailViewCol(LocaleKeys.dateRequired.tr(), moNotificationBodyList[index].dATEREQUIRED ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.lineStatus.tr(), moNotificationBodyList[index].lINESTATUS ?? ""),
ItemDetailViewCol(LocaleKeys.statusDate.tr(), moNotificationBodyList[index].sTATUSDATE ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.transactionType.tr(), moNotificationBodyList[index].tRANSACTIONTYPENAME ?? ""),
ItemDetailViewCol(LocaleKeys.operatingUnit.tr(), moNotificationBodyList[index].oPERATINGUNIT ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.organizationCode.tr(), moNotificationBodyList[index].oRGANIZATIONCODE ?? ""),
ItemDetailViewCol(LocaleKeys.organization.tr(), moNotificationBodyList[index].oRGANIZATIONNAME ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.fromSubInventory.tr(), moNotificationBodyList[index].fROMSUBINVENTORY ?? ""),
ItemDetailViewCol(LocaleKeys.fromLocator.tr(), moNotificationBodyList[index].fROMLOCATOR ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.toSubInventory.tr(), moNotificationBodyList[index].tOSUBINVENTORY ?? ""),
ItemDetailViewCol(LocaleKeys.toLocator.tr(), moNotificationBodyList[index].tOLOCATOR ?? ""),
),
ItemDetailGrid(ItemDetailViewCol(LocaleKeys.shipToLocator.tr(), moNotificationBodyList[index].sHIPTOLOCATION ?? ""), Container()),
12.height,
DefaultButton(LocaleKeys.itemHistory.tr(), () {
Navigator.pushNamed(
cxt,
AppRoutes.itemHistory,
arguments: ItemHistoryScreenParams(title: LocaleKeys.itemHistory.tr(), pItemId: moNotificationBodyList[index].iTEMID, pOrgId: moNotificationBodyList[index].oRGID),
);
})
],
).objectContainerView(title: moNotificationBodyList[index].dESCRIPTION ?? ""),
separatorBuilder: (cxt, index) => 12.height,
itemCount: moNotificationBodyList.length);
}
Widget itemCreationLinesView() {
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder:
(cxt, index) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.productName.tr(),
itemCreationLines[index].pRODUCTNAME ?? "",
),
ItemDetailViewCol(
LocaleKeys.productDescription.tr(),
itemCreationLines[index].pRODUCTDESCRIPTION ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.unitPrice.tr(),
itemCreationLines[index].uNITPRICE?.toString() ?? "",
),
ItemDetailViewCol(
LocaleKeys.manufacturerName.tr(),
itemCreationLines[index].uSERMANUFACTURERNAME ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.manufacturerPartName.tr(),
itemCreationLines[index].uSERMFGPARTNUM ?? "",
),
ItemDetailViewCol(
LocaleKeys.supplierName.tr(),
itemCreationLines[index].sUPPLIERNAME ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.supplierContact.tr(),
itemCreationLines[index].sUPPLIERCONTACT ?? "",
),
ItemDetailViewCol(
LocaleKeys.chargeToPatient.tr(),
itemCreationLines[index].cHARGETOPATIENT ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.justification.tr(),
itemCreationLines[index].jUSTIFICATION ?? "",
),
ItemDetailViewCol(
LocaleKeys.itemCode.tr(),
itemCreationLines[index].iTEMCODE ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.itemDescription.tr(),
itemCreationLines[index].iTEMDESCRIPTION ?? "",
),
ItemDetailViewCol(
LocaleKeys.groupCode.tr(),
itemCreationLines[index].iTEMGROUPCODE ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.groupDescription.tr(),
itemCreationLines[index].iTEMGROUP ?? "",
),
ItemDetailViewCol(
LocaleKeys.subgroupCode.tr(),
itemCreationLines[index].iTEMSUBGROUPCODE ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.subgroupDescription.tr(),
itemCreationLines[index].iTEMSUBGROUP ?? "",
),
ItemDetailViewCol(
LocaleKeys.primaryUOM.tr(),
itemCreationLines[index].pRIMARYUOM ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.manufacturerName.tr(),
itemCreationLines[index].sTANDARDMANUFACTURERNAME ?? "",
),
ItemDetailViewCol(
LocaleKeys.manufacturerPartName.tr(),
itemCreationLines[index].sTANDARDMFGPARTNUM ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.templateName.tr(),
itemCreationLines[index].tEMPLATENAME ?? "",
),
ItemDetailViewCol(
LocaleKeys.itemCreationStatus.tr(),
itemCreationLines[index].iTEMCREATIONSTATUS ?? "",
),
),
ItemDetailGrid(
ItemDetailViewCol(
LocaleKeys.standardizationApprovalStatus.tr(),
itemCreationLines[index].sTANDARDSTATUS ?? "",
),
ItemDetailViewCol(
LocaleKeys.standardizationApprovalRejectionReason.tr(),
itemCreationLines[index].sTANDARDREJECTREASON ?? "",
),
isItLast: true,
),
],
).objectContainerView(
title: itemCreationLines[index].iTEMDESCRIPTION!,
),
separatorBuilder: (cxt, index) => 12.height,
itemCount: itemCreationLines.length,
);
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (cxt, index) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.productName.tr(), itemCreationLines[index].pRODUCTNAME ?? ""),
ItemDetailViewCol(LocaleKeys.productDescription.tr(), itemCreationLines[index].pRODUCTDESCRIPTION ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.unitPrice.tr(), itemCreationLines[index].uNITPRICE?.toString() ?? ""),
ItemDetailViewCol(LocaleKeys.manufacturerName.tr(), itemCreationLines[index].uSERMANUFACTURERNAME ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.manufacturerPartName.tr(), itemCreationLines[index].uSERMFGPARTNUM ?? ""),
ItemDetailViewCol(LocaleKeys.supplierName.tr(), itemCreationLines[index].sUPPLIERNAME ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.supplierContact.tr(), itemCreationLines[index].sUPPLIERCONTACT ?? ""),
ItemDetailViewCol(LocaleKeys.chargeToPatient.tr(), itemCreationLines[index].cHARGETOPATIENT ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.justification.tr(), itemCreationLines[index].jUSTIFICATION ?? ""),
ItemDetailViewCol(LocaleKeys.itemCode.tr(), itemCreationLines[index].iTEMCODE ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.itemDescription.tr(), itemCreationLines[index].iTEMDESCRIPTION ?? ""),
ItemDetailViewCol(LocaleKeys.groupCode.tr(), itemCreationLines[index].iTEMGROUPCODE ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.groupDescription.tr(), itemCreationLines[index].iTEMGROUP ?? ""),
ItemDetailViewCol(LocaleKeys.subgroupCode.tr(), itemCreationLines[index].iTEMSUBGROUPCODE ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.subgroupDescription.tr(), itemCreationLines[index].iTEMSUBGROUP ?? ""),
ItemDetailViewCol(LocaleKeys.primaryUOM.tr(), itemCreationLines[index].pRIMARYUOM ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.manufacturerName.tr(), itemCreationLines[index].sTANDARDMANUFACTURERNAME ?? ""),
ItemDetailViewCol(LocaleKeys.manufacturerPartName.tr(), itemCreationLines[index].sTANDARDMFGPARTNUM ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.templateName.tr(), itemCreationLines[index].tEMPLATENAME ?? ""),
ItemDetailViewCol(LocaleKeys.itemCreationStatus.tr(), itemCreationLines[index].iTEMCREATIONSTATUS ?? ""),
),
ItemDetailGrid(
ItemDetailViewCol(LocaleKeys.standardizationApprovalStatus.tr(), itemCreationLines[index].sTANDARDSTATUS ?? ""),
ItemDetailViewCol(LocaleKeys.standardizationApprovalRejectionReason.tr(), itemCreationLines[index].sTANDARDREJECTREASON ?? ""),
isItLast: true,
),
],
).objectContainerView(title: itemCreationLines[index].iTEMDESCRIPTION!),
separatorBuilder: (cxt, index) => 12.height,
itemCount: itemCreationLines.length);
}
void getPRDetails(BuildContext context, String poLineID) async {
try {
Utils.showLoading(context);
GetPRInformationList? getPRInformationList = GetPRInformationList();
getPRInformationList = await WorkListApiClient().getPRDetailsForPO(
poLineID,
);
getPRInformationList = await WorkListApiClient().getPRDetailsForPO(poLineID);
Utils.hideLoading(context);
Navigator.pushNamed(
context,
AppRoutes.itemHistory,
arguments: ItemHistoryScreenParams(
isItemHistory: false,
isMO: false,
isPRInfo: true,
getPRInformationList: getPRInformationList,
title: "PR Details",
pItemId: 0,
pPoHeaderId: 0,
pOLineID: poLineID,
),
isItemHistory: false, isMO: false, isPRInfo: true, getPRInformationList: getPRInformationList, title: "PR Details", pItemId: 0, pPoHeaderId: 0, pOLineID: poLineID),
);
} catch (ex) {
Utils.hideLoading(context);

@ -11,7 +11,7 @@ import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:mohem_flutter_app/widgets/dynamic_forms/dynamic_textfield_widget.dart';
class WorkListAdvanceSearch extends StatefulWidget {
final Function(int, String, String, String) onSearch;
Function(int, String, String, String) onSearch;
WorkListAdvanceSearch(this.onSearch, {Key? key}) : super(key: key);

@ -47,7 +47,7 @@ class ImageOptions {
}
},
onFilesTap: () async {
FilePickerResult? result = await FilePicker.platform.pickFiles(
List<PlatformFile>? result = await FilePicker.pickFiles(
type: FileType.custom,
allowedExtensions: [
'jpg',
@ -63,8 +63,8 @@ class ImageOptions {
'zip',
],
);
List<File> files = result!.paths.map((path) => File(path!)).toList();
image(result.files.first.path.toString(), files.first);
List<File> files = result!.map((path) => File(path.uri.toFilePath())).toList();
image(result.first.path.toString(), files.first);
},
),
);
@ -141,7 +141,7 @@ void galleryImageAndroid(Function(String, File) image) async {
}
void cameraImageAndroid(Function(String, File) image) async {
File _image = File((await picker.pickImage(source: ImageSource.camera, imageQuality: 20))?.path ?? "");
File _image = File(( await picker.pickMedia())?.path ?? "");
String fileName = _image.path;
var bytes = File(fileName).readAsBytesSync();
String base64Encode = base64.encode(bytes);
@ -159,7 +159,7 @@ class _BottomSheet extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 12.0),
decoration: const BoxDecoration( borderRadius: BorderRadius.only(topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0))),
decoration: BoxDecoration( borderRadius: const BorderRadius.only(topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0))),
child: SafeArea(
top: false,
child: Column(

@ -3,7 +3,9 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:nfc_manager/nfc_manager.dart';
import 'package:nfc_manager/platform_tags.dart';
import 'package:nfc_manager/nfc_manager_android.dart';
import 'package:nfc_manager/nfc_manager_ios.dart';
import 'dart:typed_data';
void showNfcReader(BuildContext context, {required Function(String? nfcId) onNcfScan}) {
showModalBottomSheet(
@ -40,14 +42,23 @@ class _NfcLayoutState extends State<NfcLayout> {
void initState() {
super.initState();
NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async {
var f;
NfcManager.instance.startSession( pollingOptions: {NfcPollingOption.iso14443},onDiscovered: (NfcTag tag) async {
Uint8List? id;
if (Platform.isAndroid) {
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);
id = NfcTagAndroid.from(tag)?.id;
} else if (Platform.isIOS) {
id = MiFareIos.from(tag)?.identifier;
}
if (id == null) {
await NfcManager.instance.stopSession();
return;
}
String identifier = f.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
String identifier =
id.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
print(identifier);
nfcId = identifier;
setState(() {

@ -8,8 +8,6 @@ import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:shimmer/shimmer.dart';
class GetAttendanceTrackingShimmer extends StatelessWidget {
const GetAttendanceTrackingShimmer({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
@ -19,7 +17,13 @@ class GetAttendanceTrackingShimmer extends StatelessWidget {
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [BoxShadow(color: const Color(0xff000000).withOpacity(.05), blurRadius: 26, offset: const Offset(0, -3))],
boxShadow: [
BoxShadow(
color: const Color(0xff000000).withOpacity(.05),
blurRadius: 26,
offset: const Offset(0, -3),
),
],
),
child: Stack(
alignment: Alignment.center,
@ -40,8 +44,15 @@ class GetAttendanceTrackingShimmer extends StatelessWidget {
LocaleKeys.timeLeftToday.tr().toText10(color: Colors.white).toShimmer(),
9.height,
const ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(20)),
child: LinearProgressIndicator(value: 0.7, minHeight: 8, valueColor: const AlwaysStoppedAnimation<Color>(Colors.white), backgroundColor: const Color(0xff196D73)),
borderRadius: BorderRadius.all(
Radius.circular(20),
),
child: LinearProgressIndicator(
value: 0.7,
minHeight: 8,
valueColor: const AlwaysStoppedAnimation<Color>(Colors.white),
backgroundColor: const Color(0xff196D73),
),
).toShimmer(),
],
).paddingOnly(top: 12, right: 15, left: 12),
@ -52,7 +63,9 @@ class GetAttendanceTrackingShimmer extends StatelessWidget {
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [LocaleKeys.checkIn.tr().toText12(color: Colors.white).toShimmer()],
children: [
LocaleKeys.checkIn.tr().toText12(color: Colors.white).toShimmer(),
],
).paddingOnly(left: 12),
),
Container(
@ -72,8 +85,6 @@ class GetAttendanceTrackingShimmer extends StatelessWidget {
}
class MenuShimmer extends StatelessWidget {
const MenuShimmer({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
@ -81,7 +92,13 @@ class MenuShimmer extends StatelessWidget {
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [BoxShadow(color: const Color(0xff000000).withOpacity(.05), blurRadius: 26, offset: const Offset(0, -3))],
boxShadow: [
BoxShadow(
color: const Color(0xff000000).withOpacity(.05),
blurRadius: 26,
offset: const Offset(0, -3),
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -90,11 +107,14 @@ class MenuShimmer extends StatelessWidget {
LocaleKeys.workList.tr().toText12(color: Colors.white).toShimmer(),
Row(
children: [
Expanded(flex: 3, child: 123.toString().toText10(color: Colors.white, isBold: true).toShimmer()),
Expanded(
flex: 3,
child: 123.toString().toText10(color: Colors.white, isBold: true).toShimmer(),
),
12.width,
SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white).toShimmer(),
SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white).toShimmer()
],
),
)
],
).paddingOnly(left: 10, right: 10, bottom: 6, top: 6),
);
@ -102,8 +122,6 @@ class MenuShimmer extends StatelessWidget {
}
class ServicesHeaderShimmer extends StatelessWidget {
const ServicesHeaderShimmer({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
@ -113,7 +131,11 @@ class ServicesHeaderShimmer extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [LocaleKeys.otherCharges.tr().toText10().toShimmer(), 6.height, LocaleKeys.services.tr().toText12(isBold: true).toShimmer()],
children: [
LocaleKeys.otherCharges.tr().toText10().toShimmer(),
6.height,
LocaleKeys.services.tr().toText12(isBold: true).toShimmer(),
],
),
),
LocaleKeys.viewAllServices.tr().toText12(isUnderLine: true).toShimmer(),
@ -123,15 +145,19 @@ class ServicesHeaderShimmer extends StatelessWidget {
}
class ServicesMenuShimmer extends StatelessWidget {
const ServicesMenuShimmer({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [BoxShadow(color: const Color(0xff000000).withOpacity(.05), blurRadius: 26, offset: const Offset(0, -3))],
boxShadow: [
BoxShadow(
color: const Color(0xff000000).withOpacity(.05),
blurRadius: 26,
offset: const Offset(0, -3),
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -147,54 +173,21 @@ class ServicesMenuShimmer extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(child: LocaleKeys.attendance.tr().toText11(isBold: false).toShimmer()),
Expanded(
child: LocaleKeys.attendance.tr().toText11(isBold: false).toShimmer(),
),
6.width,
SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4).toShimmer(),
SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4).toShimmer()
],
),
],
),
)
],
).paddingOnly(left: 10, right: 10, bottom: 10, top: 12),
);
}
}
class GreetingCardShimmer extends StatelessWidget {
const GreetingCardShimmer({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: 140,
decoration: BoxDecoration(color: const Color(0xFFFAF4E7), border: Border.all(width: 2, color: const Color(0xFFE7E7E7)), borderRadius: BorderRadius.circular(20)),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 10),
Container(width: 65, height: 100, decoration: BoxDecoration(color: Colors.grey[300], borderRadius: BorderRadius.circular(10))).toShimmer().paddingOnly(top: 10),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(width: 150, height: 16, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(4))).toShimmer(),
const SizedBox(height: 8),
Container(width: 120, height: 16, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(4))).toShimmer(),
],
).expanded,
Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: [Container(width: 60, height: 12, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(4))).toShimmer()],
),
],
).paddingOnly(left: 8, right: 8, top: 6, bottom: 12),
).paddingOnly(top: 14, bottom: 14);
}
}
class MarathonBannerShimmer extends StatelessWidget {
const MarathonBannerShimmer({Key? key}) : super(key: key);
@ -204,7 +197,13 @@ class MarathonBannerShimmer extends StatelessWidget {
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [BoxShadow(color: const Color(0xff000000).withOpacity(.05), blurRadius: 26, offset: const Offset(0, -3))],
boxShadow: [
BoxShadow(
color: const Color(0xff000000).withOpacity(.05),
blurRadius: 26,
offset: const Offset(0, -3),
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -220,13 +219,15 @@ class MarathonBannerShimmer extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(child: LocaleKeys.attendance.tr().toText11(isBold: false).toShimmer()),
Expanded(
child: LocaleKeys.attendance.tr().toText11(isBold: false).toShimmer(),
),
6.width,
SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4).toShimmer(),
SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4).toShimmer()
],
),
],
),
)
],
).paddingOnly(left: 10, right: 10, bottom: 10, top: 12),
);
@ -242,43 +243,65 @@ class ChatHomeShimmer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Shimmer.fromColors(
baseColor: Colors.white,
highlightColor: Colors.grey.shade100,
child: ListView.builder(
itemBuilder:
(_, __) => Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
if (!isDetailedScreen) Container(width: 48.0, height: 48.0, decoration: const BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(40)))),
if (!isDetailedScreen) const Padding(padding: EdgeInsets.symmetric(horizontal: 8.0)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(width: double.infinity, height: 20.0, color: Colors.white),
const Padding(padding: EdgeInsets.symmetric(vertical: 2.0)),
Container(width: double.infinity, height: 15.0, color: Colors.white),
const Padding(padding: EdgeInsets.symmetric(vertical: 2.0)),
Container(width: 40.0, height: 10.0, color: Colors.white),
],
).expanded,
],
),
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Shimmer.fromColors(
baseColor: Colors.white,
highlightColor: Colors.grey.shade100,
child: ListView.builder(
itemBuilder: (_, __) => Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
if (!isDetailedScreen)
Container(
width: 48.0,
height: 48.0,
decoration: const BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(40))),
),
if (!isDetailedScreen)
const Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: double.infinity,
height: 20.0,
color: Colors.white,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 2.0),
),
Container(
width: double.infinity,
height: 15.0,
color: Colors.white,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 2.0),
),
Container(
width: 40.0,
height: 10.0,
color: Colors.white,
),
],
).expanded
],
),
itemCount: 6,
),
itemCount: 6,
),
),
),
),
],
),
);
],
));
}
}

@ -1,17 +1,13 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:webview_flutter/webview_flutter.dart';
// Enum for SSO types
enum SsoType { forTicket, forEnsat, forCourses }
class SsoLoginWebView extends StatefulWidget {
final String? url;
final String? jwtToken;
final SsoType ssoType;
const SsoLoginWebView({Key? key, required this.url, this.jwtToken, required this.ssoType}) : super(key: key);
SsoLoginWebView({this.url, this.jwtToken});
@override
State<SsoLoginWebView> createState() => _SsoLoginWebViewState();
@ -19,27 +15,11 @@ class SsoLoginWebView extends StatefulWidget {
class _SsoLoginWebViewState extends State<SsoLoginWebView> {
late final WebViewController _controller;
String? url;
bool _isTicketLoadingVisible = false;
void _showTicketLoading() {
if (widget.ssoType != SsoType.forTicket || _isTicketLoadingVisible || !mounted) return;
_isTicketLoadingVisible = true;
Utils.showLoading(context);
}
void _hideTicketLoading() {
if (widget.ssoType != SsoType.forTicket || !_isTicketLoadingVisible || !mounted) return;
_isTicketLoadingVisible = false;
Utils.hideLoading(context);
}
@override
void initState() {
// TODO: implement initState
super.initState();
url = widget.url ?? "";
_controller =
WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
@ -47,72 +27,35 @@ class _SsoLoginWebViewState extends State<SsoLoginWebView> {
NavigationDelegate(
onProgress: (int progress) {
print("WebView is loading (progress: $progress%)");
if (widget.ssoType == SsoType.forEnsat && progress == 100) {
Utils.hideLoading(context);
}
},
onPageStarted: (String url) {
print("Page started loading: $url");
_showTicketLoading();
},
onPageFinished: (String url) {
print("Page finished loading: $url");
_hideTicketLoading();
},
onHttpError: (HttpResponseError error) {
print("HTTP error: ${error.toString()} for URL: ${error.response!.statusCode}");
_hideTicketLoading();
},
onWebResourceError: (WebResourceError error) {
print("Web resource error: ${error.description} for URL: ${error.errorType}");
_hideTicketLoading();
},
),
);
// Load content based on SSO type
if (widget.ssoType == SsoType.forEnsat) {
// For ENSAT, just load the URL directly (GET request)
_controller.loadRequest(Uri.parse(url!));
} else {
if (widget.ssoType == SsoType.forTicket) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_showTicketLoading();
});
}
// For Paxes (Ticket, Courses), use HTML form POST
_controller.loadHtmlString(_buildHtmlContent());
}
}
// Build HTML content for the SSO form
String _buildHtmlContent() {
String formAction;
if (widget.ssoType == SsoType.forTicket) {
formAction = "https://www.paxes.com/sso/hmg";
} else {
formAction = url ?? "";
}
return '''
)
..loadHtmlString('''
<!DOCTYPE html>
<html>
<body onload="document.forms[0].submit()">
<form method="POST" action="$formAction">
<form method="POST" action="https://Paxes-release.techmaster.in/SSO/HMG">
<input type="hidden" name="JWTToken" value="${widget.jwtToken}" />
</form>
<h1></h1>
<h1>Redirecting...</h1>
</body>
</html>''';
}
@override
void dispose() {
_hideTicketLoading();
super.dispose();
</html>''');
}
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBarWidget(context, title: "MoheM", showWorkListSettingButton: false), body: SafeArea(child: WebViewWidget(controller: _controller)));
return Scaffold(appBar: AppBar(title: Text('Logging in...')), body: WebViewWidget(controller: _controller));
}
}

@ -17,8 +17,8 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 3.7.2+300082
#version: 3.9.3+1
#version: 3.7.1+300081
version: 3.9.3+1
environment:
sdk: ^3.7.0
@ -38,97 +38,98 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.4
path_provider: ^2.0.8
injector: ^2.0.0
injector: ^4.0.0
provider: ^6.0.1
easy_localization: ^3.0.0
http: ^1.4.0
permission_handler: ^12.0.1
permission_handler: ^13.0.2
flutter_svg: any
sizer: ^2.0.15
local_auth: ^2.3.0
fluttertoast: ^8.0.8
syncfusion_flutter_calendar: ^29.1.38
sizer: ^3.1.3
local_auth: ^3.0.2
fluttertoast: ^10.0.0
syncfusion_flutter_calendar: ^35.1.37
# flutter_calendar_carousel: ^2.1.0
pie_chart: ^5.1.0
shared_preferences: ^2.0.12
firebase_messaging: ^15.2.9
shimmer: ^2.0.0
logger: ^1.1.0
firebase_messaging: ^16.7.0
shimmer: ^4.0.0
logger: ^2.8.0
flutter_countdown_timer: ^4.1.0
# platform_device_id_plus: ^1.0.7
mobile_device_identifier: ^0.0.3
device_info_plus: ^11.5.0
# mobile_device_identifier: ^0.0.3
device_info_plus: ^13.2.0
image_picker: ^1.1.2
file_picker: ^8.3.1
geolocator: ^9.0.2
file_picker: ^13.1.0
geolocator: ^14.1.1
month_year_picker: ^0.5.0+1
month_picker_dialog: ^6.0.3
# open_file: ^3.2.1
open_filex: ^4.6.0
wifi_iot: ^0.3.19+1
flutter_html: ^3.0.0-alpha.6
mobile_scanner: ^6.0.10
wifi_iot: ^0.4.0
# flutter_html: ^3.0.0-alpha.6
flutter_widget_from_html: ^0.17.4
just_audio: ^0.10.6
# mobile@1234
mobile_scanner: ^7.4.2
# flutter_barcode_scanner: ^2.0.0
# qr_code_scanner: ^1.0.1
# qr_flutter: ^4.0.0
url_launcher: ^6.0.15
share_plus: ^11.0.0
# share_plus: ^13.3.0
flutter_rating_bar: ^4.0.1
auto_size_text: ^3.0.0
pull_to_refresh: ^2.0.0
fl_chart: ^0.66.0
fl_chart: ^1.2.0
# lottie json animations
lottie: any
# Marathon Card Swipe
appinio_swiper: ^1.1.1
appinio_swiper: ^2.1.1
expandable: ^5.0.1
safe_device: ^1.1.9
# networkImage
cached_network_image: ^3.2.2
cached_network_image: ^4.0.3
#Chat
signalr_netcore: ^1.3.3
logging: ^1.0.1
swipe_to: ^1.0.2
flutter_webrtc: ^0.12.11
flutter_webrtc: ^1.6.2+hotfix.3
# camera: ^0.10.3
camera: ^0.11.2
flutter_local_notifications: ^18.0.1
camera: ^0.12.1
flutter_local_notifications: ^22.3.1
#firebase_analytics: any
#Chat Voice Message Recoding & Play
audio_waveforms: ^1.3.0
rxdart: ^0.27.7
#Speech to Text
speech_to_text: ^7.0.0
audio_waveforms: ^2.0.2
rxdart: ^0.28.0
#Encryption
flutter_des: ^2.1.0
flutter_des: ^3.0.0
video_player: 2.7.0
just_audio: ^0.9.30
video_player: ^2.14.0
# just_audio: ^0.9.30
# safe_device: ^1.1.2
flutter_layout_grid: ^2.0.1
jiffy: ^6.4.4
#Huawei Dependencies
# huawei_hmsavailability: ^6.6.0+300
# huawei_location: 6.0.0+302
huawei_location: ^6.14.2+301
huawei_location: ^6.17.0+300
# huawei_push: ^6.7.0+300
huawei_map:
git:
url: https://github.com/fleoparra/hms-flutter-plugin.git
path: flutter-hms-map
huawei_push:
git:
url: https://github.com/crasowas/hms-flutter-plugin.git
path: flutter-hms-push
firebase_crashlytics: ^4.3.9
# huawei_map:
# git:
# url: https://github.com/fleoparra/hms-flutter-plugin.git
# path: flutter-hms-map
huawei_push: ^6.15.0+300
# git:
# url: https://github.com/crasowas/hms-flutter-plugin.git
# path: flutter-hms-push
firebase_crashlytics: ^5.4.0
#Items for sale Image Carousel Slider
carousel_slider: ^5.1.1
@ -136,15 +137,17 @@ dependencies:
#Huawei Specified
# store_checker: ^1.1.0
google_api_availability: ^5.0.1
google_maps_flutter_web: ^0.5.4
in_app_update: 4.1.0
google_maps_flutter_web: ^0.6.4+1
in_app_update: 5.0.0
flutter_inappwebview: ^6.1.5
flutter_blurhash: ^0.8.0
flutter_blurhash: ^0.9.1
#todo its for temporary purpose, later will remove this.
dotted_border: ^2.0.0+3
dotted_border: ^3.1.0
webview_flutter: ^4.13.0
nfc_manager: ^3.2.0
nfc_manager: ^4.2.1
speech_to_text: ^7.3.0
# saf: ^1.0.3+4
@ -163,7 +166,7 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^1.0.4
flutter_lints: ^6.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

Loading…
Cancel
Save