Compare commits
No commits in common. "master" and "main" have entirely different histories.
98
.gitignore
vendored
Executable file → Normal file
@ -1,68 +1,50 @@
|
|||||||
# Miscellaneous
|
# These are some examples of commonly ignored file patterns.
|
||||||
|
# You should customize this list as applicable to your project.
|
||||||
|
# Learn more about .gitignore:
|
||||||
|
# https://www.atlassian.com/git/tutorials/saving-changes/gitignore
|
||||||
|
|
||||||
|
# Node artifact files
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Compiled Java class files
|
||||||
*.class
|
*.class
|
||||||
|
|
||||||
|
# Compiled Python bytecode
|
||||||
|
*.py[cod]
|
||||||
|
|
||||||
|
# Log files
|
||||||
*.log
|
*.log
|
||||||
*.pyc
|
|
||||||
*.swp
|
|
||||||
.DS_Store
|
|
||||||
.atom/
|
|
||||||
.buildlog/
|
|
||||||
.history
|
|
||||||
.svn/
|
|
||||||
.env
|
|
||||||
migrate_working_dir/
|
|
||||||
|
|
||||||
# IntelliJ related
|
# Package files
|
||||||
*.iml
|
*.jar
|
||||||
*.lock
|
|
||||||
*.packages
|
# Maven
|
||||||
*.ipr
|
target/
|
||||||
*.iws
|
dist/
|
||||||
|
|
||||||
|
# JetBrains IDE
|
||||||
.idea/
|
.idea/
|
||||||
.vscode/
|
|
||||||
|
|
||||||
# The .vscode folder contains launch configuration and tasks you configure in
|
# Unit test reports
|
||||||
# VS Code which you may wish to be included in version control, so this line
|
TEST*.xml
|
||||||
# is commented out by default.
|
|
||||||
#.vscode/
|
|
||||||
|
|
||||||
# Flutter/Dart/Pub related
|
# Generated by MacOS
|
||||||
**/doc/api/
|
.DS_Store
|
||||||
**/ios/Flutter/.last_build_id
|
|
||||||
.dart_tool/
|
|
||||||
.flutter-plugins
|
|
||||||
.flutter-plugins-dependencies
|
|
||||||
.pub-cache/
|
|
||||||
.pub/
|
|
||||||
/build/
|
|
||||||
|
|
||||||
# Android specific files
|
# Generated by Windows
|
||||||
*.android/
|
Thumbs.db
|
||||||
*.android.iml
|
|
||||||
*.apk
|
|
||||||
*.ap_
|
|
||||||
|
|
||||||
# iOS specific files
|
# Applications
|
||||||
*.ios/
|
*.app
|
||||||
*.ios.iml
|
*.exe
|
||||||
*.ipa
|
*.war
|
||||||
*.xcworkspace
|
|
||||||
*.xcodeproj/
|
|
||||||
*.xcuserdatad/
|
|
||||||
|
|
||||||
# macOS specific files
|
# Large media files
|
||||||
.idea/workspace.xml
|
*.mp4
|
||||||
|
*.tiff
|
||||||
|
*.avi
|
||||||
|
*.flv
|
||||||
|
*.mov
|
||||||
|
*.wmv
|
||||||
|
|
||||||
|
|
||||||
# Dependency lock files
|
|
||||||
.pubspec.lock
|
|
||||||
|
|
||||||
# Symbolication related
|
|
||||||
app.*.symbols
|
|
||||||
|
|
||||||
# Obfuscation related
|
|
||||||
app.*.map.json
|
|
||||||
|
|
||||||
# Android Studio will place build artifacts here
|
|
||||||
/android/app/debug
|
|
||||||
/android/app/profile
|
|
||||||
/android/app/release
|
|
||||||
|
|||||||
45
.metadata
@ -1,45 +0,0 @@
|
|||||||
# This file tracks properties of this Flutter project.
|
|
||||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
|
||||||
#
|
|
||||||
# This file should be version controlled and should not be manually edited.
|
|
||||||
|
|
||||||
version:
|
|
||||||
revision: "7482962148e8d758338d8a28f589f317e1e42ba4"
|
|
||||||
channel: "stable"
|
|
||||||
|
|
||||||
project_type: app
|
|
||||||
|
|
||||||
# Tracks metadata for the flutter migrate command
|
|
||||||
migration:
|
|
||||||
platforms:
|
|
||||||
- platform: root
|
|
||||||
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
|
|
||||||
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
|
|
||||||
- platform: android
|
|
||||||
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
|
|
||||||
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
|
|
||||||
- platform: ios
|
|
||||||
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
|
|
||||||
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
|
|
||||||
- platform: linux
|
|
||||||
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
|
|
||||||
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
|
|
||||||
- platform: macos
|
|
||||||
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
|
|
||||||
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
|
|
||||||
- platform: web
|
|
||||||
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
|
|
||||||
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
|
|
||||||
- platform: windows
|
|
||||||
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
|
|
||||||
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
|
|
||||||
|
|
||||||
# User provided section
|
|
||||||
|
|
||||||
# List of Local paths (relative to this file) that should be
|
|
||||||
# ignored by the migrate tool.
|
|
||||||
#
|
|
||||||
# Files that are not part of the templates will be ignored by default.
|
|
||||||
unmanaged_files:
|
|
||||||
- 'lib/main.dart'
|
|
||||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
|
||||||
33
README.md
Executable file → Normal file
@ -1,16 +1,29 @@
|
|||||||
# nhancepolicy
|
# README #
|
||||||
|
|
||||||
A new Flutter project.
|
This README would normally document whatever steps are necessary to get your application up and running.
|
||||||
|
|
||||||
## Getting Started
|
### What is this repository for? ###
|
||||||
|
|
||||||
This project is a starting point for a Flutter application.
|
* Quick summary
|
||||||
|
* Version
|
||||||
|
* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
|
||||||
|
|
||||||
A few resources to get you started if this is your first Flutter project:
|
### How do I get set up? ###
|
||||||
|
|
||||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
* Summary of set up
|
||||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
* Configuration
|
||||||
|
* Dependencies
|
||||||
|
* Database configuration
|
||||||
|
* How to run tests
|
||||||
|
* Deployment instructions
|
||||||
|
|
||||||
For help getting started with Flutter development, view the
|
### Contribution guidelines ###
|
||||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
|
||||||
samples, guidance on mobile development, and a full API reference.
|
* Writing tests
|
||||||
|
* Code review
|
||||||
|
* Other guidelines
|
||||||
|
|
||||||
|
### Who do I talk to? ###
|
||||||
|
|
||||||
|
* Repo owner or admin
|
||||||
|
* Other community or team contact
|
||||||
@ -1,28 +0,0 @@
|
|||||||
# This file configures the analyzer, which statically analyzes Dart code to
|
|
||||||
# check for errors, warnings, and lints.
|
|
||||||
#
|
|
||||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
|
||||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
|
||||||
# invoked from the command line by running `flutter analyze`.
|
|
||||||
|
|
||||||
# The following line activates a set of recommended lints for Flutter apps,
|
|
||||||
# packages, and plugins designed to encourage good coding practices.
|
|
||||||
include: package:flutter_lints/flutter.yaml
|
|
||||||
|
|
||||||
linter:
|
|
||||||
# The lint rules applied to this project can be customized in the
|
|
||||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
|
||||||
# included above or to enable additional rules. A list of all available lints
|
|
||||||
# and their documentation is published at https://dart.dev/lints.
|
|
||||||
#
|
|
||||||
# Instead of disabling a lint rule for the entire project in the
|
|
||||||
# section below, it can also be suppressed for a single line of code
|
|
||||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
|
||||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
|
||||||
# producing the lint.
|
|
||||||
rules:
|
|
||||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
|
||||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
||||||
13
android/.gitignore
vendored
@ -1,13 +0,0 @@
|
|||||||
gradle-wrapper.jar
|
|
||||||
/.gradle
|
|
||||||
/captures/
|
|
||||||
/gradlew
|
|
||||||
/gradlew.bat
|
|
||||||
/local.properties
|
|
||||||
GeneratedPluginRegistrant.java
|
|
||||||
|
|
||||||
# Remember to never publicly share your keystore.
|
|
||||||
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
|
|
||||||
key.properties
|
|
||||||
**/*.keystore
|
|
||||||
**/*.jks
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id "com.android.application"
|
|
||||||
id "kotlin-android"
|
|
||||||
id "dev.flutter.flutter-gradle-plugin"
|
|
||||||
}
|
|
||||||
|
|
||||||
def localProperties = new Properties()
|
|
||||||
def localPropertiesFile = rootProject.file('local.properties')
|
|
||||||
if (localPropertiesFile.exists()) {
|
|
||||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
|
||||||
localProperties.load(reader)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
|
||||||
if (flutterVersionCode == null) {
|
|
||||||
flutterVersionCode = '1'
|
|
||||||
}
|
|
||||||
|
|
||||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
|
||||||
if (flutterVersionName == null) {
|
|
||||||
flutterVersionName = '1.0'
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace "com.example.nhance_app_pwa"
|
|
||||||
compileSdk flutter.compileSdkVersion
|
|
||||||
ndkVersion flutter.ndkVersion
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = '1.8'
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
|
||||||
applicationId "com.example.nhance_app_pwa"
|
|
||||||
// You can update the following values to match your application needs.
|
|
||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
|
||||||
minSdkVersion flutter.minSdkVersion
|
|
||||||
targetSdkVersion flutter.targetSdkVersion
|
|
||||||
versionCode flutterVersionCode.toInteger()
|
|
||||||
versionName flutterVersionName
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
// TODO: Add your own signing config for the release build.
|
|
||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
|
||||||
signingConfig signingConfigs.debug
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
flutter {
|
|
||||||
source '../..'
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"project_info": {
|
|
||||||
"project_number": "508866094093",
|
|
||||||
"project_id": "nhance-app-4fedb",
|
|
||||||
"storage_bucket": "nhance-app-4fedb.appspot.com"
|
|
||||||
},
|
|
||||||
"client": [
|
|
||||||
{
|
|
||||||
"client_info": {
|
|
||||||
"mobilesdk_app_id": "1:508866094093:android:9969e766daa5b770366144",
|
|
||||||
"android_client_info": {
|
|
||||||
"package_name": "com.example.nhance_app_pwa"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"oauth_client": [],
|
|
||||||
"api_key": [
|
|
||||||
{
|
|
||||||
"current_key": "AIzaSyAZdKJco4fe0e1HP1A249BVZzA3WMYb_3g"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"services": {
|
|
||||||
"appinvite_service": {
|
|
||||||
"other_platform_oauth_client": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"configuration_version": "1"
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<!-- The INTERNET permission is required for development. Specifically,
|
|
||||||
the Flutter tool needs it to communicate with the running application
|
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
|
||||||
-->
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
|
||||||
</manifest>
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.nhance_app_pwa">
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
|
||||||
<application
|
|
||||||
android:label="nhancepolicy"
|
|
||||||
android:name="${applicationName}"
|
|
||||||
android:icon="@mipmap/ic_launcher">
|
|
||||||
<activity
|
|
||||||
android:name=".MainActivity"
|
|
||||||
android:exported="true"
|
|
||||||
android:launchMode="singleTop"
|
|
||||||
android:theme="@style/LaunchTheme"
|
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
|
||||||
android:hardwareAccelerated="true"
|
|
||||||
android:windowSoftInputMode="adjustResize">
|
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
|
||||||
the Android process has started. This theme is visible to the user
|
|
||||||
while the Flutter UI initializes. After that, this theme continues
|
|
||||||
to determine the Window background behind the Flutter UI. -->
|
|
||||||
<meta-data
|
|
||||||
android:name="io.flutter.embedding.android.NormalTheme"
|
|
||||||
android:resource="@style/NormalTheme"
|
|
||||||
/>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
<!-- Don't delete the meta-data below.
|
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
|
||||||
<meta-data
|
|
||||||
android:name="flutterEmbedding"
|
|
||||||
android:value="2" />
|
|
||||||
</application>
|
|
||||||
<!-- Required to query activities that can process text, see:
|
|
||||||
https://developer.android.com/training/package-visibility?hl=en and
|
|
||||||
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
|
||||||
|
|
||||||
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
|
||||||
<queries>
|
|
||||||
<intent>
|
|
||||||
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
|
||||||
<data android:mimeType="text/plain"/>
|
|
||||||
</intent>
|
|
||||||
</queries>
|
|
||||||
</manifest>
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
package com.example.nhance_app_pwa
|
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
|
||||||
|
|
||||||
class MainActivity: FlutterActivity()
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Modify this file to customize your launch splash screen -->
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:drawable="?android:colorBackground" />
|
|
||||||
|
|
||||||
<!-- You can insert your own image assets here -->
|
|
||||||
<!-- <item>
|
|
||||||
<bitmap
|
|
||||||
android:gravity="center"
|
|
||||||
android:src="@mipmap/launch_image" />
|
|
||||||
</item> -->
|
|
||||||
</layer-list>
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Modify this file to customize your launch splash screen -->
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:drawable="@android:color/white" />
|
|
||||||
|
|
||||||
<!-- You can insert your own image assets here -->
|
|
||||||
<!-- <item>
|
|
||||||
<bitmap
|
|
||||||
android:gravity="center"
|
|
||||||
android:src="@mipmap/launch_image" />
|
|
||||||
</item> -->
|
|
||||||
</layer-list>
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 996 B |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
|
||||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
|
||||||
<!-- Show a splash screen on the activity. Automatically removed when
|
|
||||||
the Flutter engine draws its first frame -->
|
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
|
||||||
</style>
|
|
||||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
|
||||||
This theme determines the color of the Android Window while your
|
|
||||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
|
||||||
running.
|
|
||||||
|
|
||||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
|
||||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
|
||||||
<item name="android:windowBackground">?android:colorBackground</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
|
||||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
|
||||||
<!-- Show a splash screen on the activity. Automatically removed when
|
|
||||||
the Flutter engine draws its first frame -->
|
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
|
||||||
</style>
|
|
||||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
|
||||||
This theme determines the color of the Android Window while your
|
|
||||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
|
||||||
running.
|
|
||||||
|
|
||||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
|
||||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
|
||||||
<item name="android:windowBackground">?android:colorBackground</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<!-- The INTERNET permission is required for development. Specifically,
|
|
||||||
the Flutter tool needs it to communicate with the running application
|
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
|
||||||
-->
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
|
||||||
</manifest>
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
allprojects {
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rootProject.buildDir = '../build'
|
|
||||||
subprojects {
|
|
||||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
|
||||||
}
|
|
||||||
subprojects {
|
|
||||||
project.evaluationDependsOn(':app')
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register("clean", Delete) {
|
|
||||||
delete rootProject.buildDir
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
org.gradle.jvmargs=-Xmx4G
|
|
||||||
android.useAndroidX=true
|
|
||||||
android.enableJetifier=true
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
|
||||||
distributionPath=wrapper/dists
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
pluginManagement {
|
|
||||||
def flutterSdkPath = {
|
|
||||||
def properties = new Properties()
|
|
||||||
file("local.properties").withInputStream { properties.load(it) }
|
|
||||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
|
||||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
|
||||||
return flutterSdkPath
|
|
||||||
}
|
|
||||||
settings.ext.flutterSdkPath = flutterSdkPath()
|
|
||||||
|
|
||||||
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
mavenCentral()
|
|
||||||
gradlePluginPortal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
|
||||||
id "com.android.application" version "7.3.0" apply false
|
|
||||||
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
|
|
||||||
}
|
|
||||||
|
|
||||||
include ":app"
|
|
||||||
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 99 B |
|
Before Width: | Height: | Size: 840 B |
BIN
assets/claim.png
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 331 B |
|
Before Width: | Height: | Size: 283 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
BIN
assets/ecard.png
|
Before Width: | Height: | Size: 218 B |
|
Before Width: | Height: | Size: 476 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 833 KiB |
|
Before Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 493 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 285 KiB |
@ -1,4 +0,0 @@
|
|||||||
description: This file stores settings for Dart & Flutter DevTools.
|
|
||||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
|
||||||
extensions:
|
|
||||||
- shared_preferences: true
|
|
||||||
34
ios/.gitignore
vendored
@ -1,34 +0,0 @@
|
|||||||
**/dgph
|
|
||||||
*.mode1v3
|
|
||||||
*.mode2v3
|
|
||||||
*.moved-aside
|
|
||||||
*.pbxuser
|
|
||||||
*.perspectivev3
|
|
||||||
**/*sync/
|
|
||||||
.sconsign.dblite
|
|
||||||
.tags*
|
|
||||||
**/.vagrant/
|
|
||||||
**/DerivedData/
|
|
||||||
Icon?
|
|
||||||
**/Pods/
|
|
||||||
**/.symlinks/
|
|
||||||
profile
|
|
||||||
xcuserdata
|
|
||||||
**/.generated/
|
|
||||||
Flutter/App.framework
|
|
||||||
Flutter/Flutter.framework
|
|
||||||
Flutter/Flutter.podspec
|
|
||||||
Flutter/Generated.xcconfig
|
|
||||||
Flutter/ephemeral/
|
|
||||||
Flutter/app.flx
|
|
||||||
Flutter/app.zip
|
|
||||||
Flutter/flutter_assets/
|
|
||||||
Flutter/flutter_export_environment.sh
|
|
||||||
ServiceDefinitions.json
|
|
||||||
Runner/GeneratedPluginRegistrant.*
|
|
||||||
|
|
||||||
# Exceptions to above rules.
|
|
||||||
!default.mode1v3
|
|
||||||
!default.mode2v3
|
|
||||||
!default.pbxuser
|
|
||||||
!default.perspectivev3
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>en</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>App</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>io.flutter.flutter.app</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>App</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>FMWK</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>MinimumOSVersion</key>
|
|
||||||
<string>12.0</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
|
||||||
#include "Generated.xcconfig"
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
|
||||||
#include "Generated.xcconfig"
|
|
||||||
43
ios/Podfile
@ -1,43 +0,0 @@
|
|||||||
# Uncomment this line to define a global platform for your project
|
|
||||||
# platform :ios, '12.0'
|
|
||||||
|
|
||||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
|
||||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
|
||||||
|
|
||||||
project 'Runner', {
|
|
||||||
'Debug' => :debug,
|
|
||||||
'Profile' => :release,
|
|
||||||
'Release' => :release,
|
|
||||||
}
|
|
||||||
|
|
||||||
def flutter_root
|
|
||||||
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
|
|
||||||
unless File.exist?(generated_xcode_build_settings_path)
|
|
||||||
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
|
|
||||||
end
|
|
||||||
|
|
||||||
File.foreach(generated_xcode_build_settings_path) do |line|
|
|
||||||
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
|
||||||
return matches[1].strip if matches
|
|
||||||
end
|
|
||||||
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
|
|
||||||
end
|
|
||||||
|
|
||||||
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
|
||||||
|
|
||||||
flutter_ios_podfile_setup
|
|
||||||
|
|
||||||
target 'Runner' do
|
|
||||||
use_frameworks!
|
|
||||||
|
|
||||||
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
|
||||||
target 'RunnerTests' do
|
|
||||||
inherit! :search_paths
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
post_install do |installer|
|
|
||||||
installer.pods_project.targets.each do |target|
|
|
||||||
flutter_additional_ios_build_settings(target)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
import UIKit
|
|
||||||
import Flutter
|
|
||||||
|
|
||||||
@UIApplicationMain
|
|
||||||
@objc class AppDelegate: FlutterAppDelegate {
|
|
||||||
override func application(
|
|
||||||
_ application: UIApplication,
|
|
||||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
|
||||||
) -> Bool {
|
|
||||||
GeneratedPluginRegistrant.register(with: self)
|
|
||||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,122 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-20x20@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-20x20@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-40x40@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-40x40@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "60x60",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-60x60@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "60x60",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-60x60@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-20x20@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-20x20@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-29x29@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-29x29@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-40x40@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-40x40@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "76x76",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-76x76@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "76x76",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-76x76@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "83.5x83.5",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "1024x1024",
|
|
||||||
"idiom" : "ios-marketing",
|
|
||||||
"filename" : "Icon-App-1024x1024@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 494 B |
|
Before Width: | Height: | Size: 772 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 663 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 772 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "LaunchImage.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "LaunchImage@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "LaunchImage@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 68 B |
|
Before Width: | Height: | Size: 68 B |
|
Before Width: | Height: | Size: 68 B |
@ -1,5 +0,0 @@
|
|||||||
# Launch Screen Assets
|
|
||||||
|
|
||||||
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
|
|
||||||
|
|
||||||
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="iOS"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
|
|
||||||
</dependencies>
|
|
||||||
<scenes>
|
|
||||||
<!--View Controller-->
|
|
||||||
<scene sceneID="EHf-IW-A2E">
|
|
||||||
<objects>
|
|
||||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
|
||||||
<layoutGuides>
|
|
||||||
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
|
|
||||||
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
|
|
||||||
</layoutGuides>
|
|
||||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
|
||||||
</imageView>
|
|
||||||
</subviews>
|
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
|
|
||||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="53" y="375"/>
|
|
||||||
</scene>
|
|
||||||
</scenes>
|
|
||||||
<resources>
|
|
||||||
<image name="LaunchImage" width="168" height="185"/>
|
|
||||||
</resources>
|
|
||||||
</document>
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="iOS"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
|
||||||
</dependencies>
|
|
||||||
<scenes>
|
|
||||||
<!--Flutter View Controller-->
|
|
||||||
<scene sceneID="tne-QT-ifu">
|
|
||||||
<objects>
|
|
||||||
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
|
|
||||||
<layoutGuides>
|
|
||||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
|
||||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
|
||||||
</layoutGuides>
|
|
||||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
|
||||||
</view>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
</scene>
|
|
||||||
</scenes>
|
|
||||||
</document>
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>Nhancepolicy</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>nhancepolicy</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
|
||||||
<key>LSRequiresIPhoneOS</key>
|
|
||||||
<true/>
|
|
||||||
<key>UILaunchStoryboardName</key>
|
|
||||||
<string>LaunchScreen</string>
|
|
||||||
<key>UIMainStoryboardFile</key>
|
|
||||||
<string>Main</string>
|
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
||||||
</array>
|
|
||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
||||||
</array>
|
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
|
||||||
<true/>
|
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@ -1 +0,0 @@
|
|||||||
#import "GeneratedPluginRegistrant.h"
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
import Flutter
|
|
||||||
import UIKit
|
|
||||||
import XCTest
|
|
||||||
|
|
||||||
class RunnerTests: XCTestCase {
|
|
||||||
|
|
||||||
func testExample() {
|
|
||||||
// If you add code to the Runner application, consider adding tests here.
|
|
||||||
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
4746
lib/addons.dart
@ -1,67 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
|
|
||||||
class BranchCard extends StatelessWidget {
|
|
||||||
final String clientName;
|
|
||||||
final String branchName;
|
|
||||||
final bool isSelected;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
const BranchCard({
|
|
||||||
Key? key,
|
|
||||||
required this.clientName,
|
|
||||||
required this.branchName,
|
|
||||||
required this.isSelected,
|
|
||||||
required this.onTap,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Tooltip(
|
|
||||||
message: clientName, // 🖱 hover shows full name
|
|
||||||
waitDuration: const Duration(milliseconds: 400),
|
|
||||||
child: InkWell(
|
|
||||||
onTap: onTap,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color:
|
|
||||||
isSelected ? const Color(0xFF00999E) : const Color(0xFFF0F9F9),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
border: Border.all(
|
|
||||||
color: const Color(0xFF00999E),
|
|
||||||
width: 1.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
clientName,
|
|
||||||
maxLines: 1, // ✅ SINGLE LINE
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
fontSize: 13,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: isSelected ? Colors.white : const Color(0xFF00999E),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
branchName,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
fontSize: 11,
|
|
||||||
color: isSelected ? Colors.white70 : Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,220 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import '../customAppBar/base_layout.dart';
|
|
||||||
import '../customAppBar/toastHelper.dart';
|
|
||||||
import '../service/api_service.dart';
|
|
||||||
import '../service/secure_pop_scope.dart';
|
|
||||||
import '../service/token_storage_service.dart';
|
|
||||||
import 'branch_card_widget.dart';
|
|
||||||
import 'dart:html' as html;
|
|
||||||
|
|
||||||
class BranchSelectionPage extends StatefulWidget {
|
|
||||||
const BranchSelectionPage({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<BranchSelectionPage> createState() => _BranchSelectionPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BranchSelectionPageState extends State<BranchSelectionPage> {
|
|
||||||
List<Map<String, dynamic>> branches = [];
|
|
||||||
int? selectedIndex;
|
|
||||||
|
|
||||||
final tokenStorage = TokenStorageService();
|
|
||||||
late ApiService apiService;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
apiService = ApiService(context);
|
|
||||||
html.window.onPopState.listen((event) async {
|
|
||||||
final shouldLogout = await _showLogoutDialog();
|
|
||||||
if (shouldLogout) {
|
|
||||||
await apiService.logout();
|
|
||||||
if (!mounted) return;
|
|
||||||
Navigator.pushNamedAndRemoveUntil(
|
|
||||||
context,
|
|
||||||
'hrLogin',
|
|
||||||
(route) => false,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Push state back to prevent browser navigation
|
|
||||||
html.window.history.pushState(null, '', html.window.location.href);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
_loadBranches();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _loadBranches() {
|
|
||||||
setState(() {
|
|
||||||
branches = tokenStorage.getCombinedBranches();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _selectBranch(int index) {
|
|
||||||
setState(() => selectedIndex = index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _handleNext() async {
|
|
||||||
if (selectedIndex == null) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
const SnackBar(content: Text('Please select a branch')),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final selectedBranch = branches[selectedIndex!];
|
|
||||||
await tokenStorage.saveSelectedBranch(selectedBranch);
|
|
||||||
|
|
||||||
final decodedToken = tokenStorage.getDecodedToken();
|
|
||||||
final token = tokenStorage.getCurrentToken();
|
|
||||||
|
|
||||||
if (decodedToken == null || token == null) {
|
|
||||||
ToastHelper.showErrorToast(context, 'Invalid token data');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 🔐 Save decoded values securely
|
|
||||||
await tokenStorage.saveDecodedSessionData(decodedToken, token);
|
|
||||||
|
|
||||||
// ToastHelper.showSuccessToast(context, 'Successfully Login');
|
|
||||||
|
|
||||||
if (!mounted) return;
|
|
||||||
Navigator.pushReplacementNamed(context, 'policies');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> _showLogoutDialog() async {
|
|
||||||
return await showDialog<bool>(
|
|
||||||
context: context,
|
|
||||||
barrierDismissible: false,
|
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
title: Text("Confirm Logout"),
|
|
||||||
content: Text("Do you want to logout?"),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.of(context).pop(false),
|
|
||||||
child: Text("Cancel"),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.of(context).pop(true),
|
|
||||||
child: Text("Logout"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
) ??
|
|
||||||
false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return BaseLayout(
|
|
||||||
child: SecurePopScope(
|
|
||||||
child: _buildContent(context),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildContent(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: const Color(0xFFF5F7F7),
|
|
||||||
body: LayoutBuilder(
|
|
||||||
builder: (context, constraints) {
|
|
||||||
final width = constraints.maxWidth;
|
|
||||||
|
|
||||||
/// 🔹 RESPONSIVE BREAKPOINTS
|
|
||||||
int crossAxisCount;
|
|
||||||
if (width < 600) {
|
|
||||||
crossAxisCount = 1;
|
|
||||||
} else if (width < 900) {
|
|
||||||
crossAxisCount = 2;
|
|
||||||
} else {
|
|
||||||
crossAxisCount = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SingleChildScrollView(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: width * 0.08,
|
|
||||||
vertical: 30,
|
|
||||||
),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(24),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.05),
|
|
||||||
blurRadius: 12,
|
|
||||||
offset: const Offset(0, 4),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'Select Client',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
/// 🔹 GRID
|
|
||||||
GridView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
itemCount: branches.length,
|
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: crossAxisCount,
|
|
||||||
mainAxisExtent: 90, // 🔥 FIXED HEIGHT
|
|
||||||
mainAxisSpacing: 15,
|
|
||||||
crossAxisSpacing: 15,
|
|
||||||
),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final branch = branches[index];
|
|
||||||
return BranchCard(
|
|
||||||
clientName: branch['client_name'] ??
|
|
||||||
'Unknown Client kjbgjsk jsdhbjbf sdjfjbds fdjsgjdbs gdsjbgjds',
|
|
||||||
branchName: branch['branch_name'] ?? 'Unknown Branch',
|
|
||||||
isSelected: selectedIndex == index,
|
|
||||||
onTap: () => _selectBranch(index),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
|
|
||||||
/// 🔹 NEXT BUTTON
|
|
||||||
Center(
|
|
||||||
child: SizedBox(
|
|
||||||
width: 140,
|
|
||||||
height: 46,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: _handleNext,
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: const Color(0xFFFF6B35),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'Next',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
// lib/environment.dart
|
|
||||||
enum Flavor { dev, uat, prod, prod1 }
|
|
||||||
|
|
||||||
class Environment {
|
|
||||||
static Flavor flavor = Flavor.dev; // overwritten by each main_*.dart
|
|
||||||
|
|
||||||
static bool get isProd => flavor == Flavor.prod || flavor == Flavor.prod1;
|
|
||||||
|
|
||||||
static String get apiUrl {
|
|
||||||
switch (flavor) {
|
|
||||||
case Flavor.dev:
|
|
||||||
return "https://venbait.in/enrollment/employeeRest/";
|
|
||||||
case Flavor.uat:
|
|
||||||
return "https://appstage.nhanceindia.in/apex/employeeRest/";
|
|
||||||
case Flavor.prod:
|
|
||||||
return "https://app.nhanceindia.in/enrolment/employeeRest/";
|
|
||||||
case Flavor.prod1:
|
|
||||||
return "https://app.nhanceindia.in/enrolment/employeeRest/";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String get apiUrlPost {
|
|
||||||
switch (flavor) {
|
|
||||||
case Flavor.dev:
|
|
||||||
return "https://venbait.in/nhance/dev/employeeRest/";
|
|
||||||
case Flavor.uat:
|
|
||||||
return "https://appstage.nhanceindia.in/nexus/employeeRest/";
|
|
||||||
case Flavor.prod:
|
|
||||||
return "https://app.nhanceindia.in/zenith/employeeRest/";
|
|
||||||
case Flavor.prod1:
|
|
||||||
return "https://app.nhanceindia.in/zenith/employeeRest/";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String get baseHref {
|
|
||||||
switch (flavor) {
|
|
||||||
case Flavor.dev:
|
|
||||||
return "/nhance/hr/dev/";
|
|
||||||
case Flavor.uat:
|
|
||||||
return "/";
|
|
||||||
case Flavor.prod:
|
|
||||||
return "/hr/";
|
|
||||||
case Flavor.prod1:
|
|
||||||
return "/";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String getPageTitle(String? routeName) {
|
|
||||||
String base = "Nhance HR";
|
|
||||||
if (isProd) base = "Nhance HR"; // You can differentiate names if needed
|
|
||||||
|
|
||||||
switch (routeName) {
|
|
||||||
case 'claimsOverviewDashboard':
|
|
||||||
return "$base - Dashboard";
|
|
||||||
case 'policies':
|
|
||||||
return "$base - Policies";
|
|
||||||
case 'pendingDependentApproval':
|
|
||||||
return "$base - Approvals";
|
|
||||||
case 'CdPoliciesList':
|
|
||||||
return "$base - CD Details";
|
|
||||||
case 'ClaimsPolicies':
|
|
||||||
return "$base - Claims";
|
|
||||||
case 'hrLogin':
|
|
||||||
return "$base - Login";
|
|
||||||
case 'cdTransactionDetails':
|
|
||||||
return "$base - Transaction Details";
|
|
||||||
case 'hrPolicyDetails':
|
|
||||||
return "$base - Member Details";
|
|
||||||
case 'preFileUpload':
|
|
||||||
return "$base - Member Upload";
|
|
||||||
case 'postFileUpload':
|
|
||||||
return "$base - Member Upload";
|
|
||||||
default:
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
import '../main.dart';
|
|
||||||
import 'environment.dart';
|
|
||||||
|
|
||||||
Future<void> main() async {
|
|
||||||
Environment.flavor = Flavor.dev;
|
|
||||||
await startApp();
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
import '../main.dart';
|
|
||||||
import 'environment.dart';
|
|
||||||
|
|
||||||
Future<void> main() async {
|
|
||||||
Environment.flavor = Flavor.prod;
|
|
||||||
await startApp();
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
import '../main.dart';
|
|
||||||
import 'environment.dart';
|
|
||||||
|
|
||||||
Future<void> main() async {
|
|
||||||
Environment.flavor = Flavor.prod1;
|
|
||||||
await startApp();
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
import '../main.dart';
|
|
||||||
import 'environment.dart';
|
|
||||||
|
|
||||||
Future<void> main() async {
|
|
||||||
Environment.flavor = Flavor.uat;
|
|
||||||
await startApp();
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'top_app_bar.dart';
|
|
||||||
import 'side_bar.dart';
|
|
||||||
import '../config/environment.dart';
|
|
||||||
|
|
||||||
class BaseLayout extends StatelessWidget {
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
const BaseLayout({super.key, required this.child});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
// 1. Capture the current route name from the navigation context
|
|
||||||
final String? routeName = ModalRoute.of(context)?.settings.name;
|
|
||||||
|
|
||||||
// 2. Use the Title widget to communicate with the browser's tab
|
|
||||||
return Title(
|
|
||||||
title: Environment.getPageTitle(routeName),
|
|
||||||
color: const Color(0xFF00999E), // Required, usually matches your theme
|
|
||||||
child: Scaffold(
|
|
||||||
appBar: const NhanceTopBar(),
|
|
||||||
body: Row(
|
|
||||||
children: [
|
|
||||||
const NhanceSideBar(),
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
color: const Color(0xFFF5F7F7),
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,204 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:adaptive_navbar/adaptive_navbar.dart';
|
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
||||||
import 'package:nhancepolicy/customAppBar/toastHelper.dart';
|
|
||||||
import 'package:nhancepolicy/responsive.dart';
|
|
||||||
import 'package:nhancepolicy/responsive.dart';
|
|
||||||
|
|
||||||
import '../service/api_service.dart';
|
|
||||||
import '../service/token_storage_service.dart';
|
|
||||||
import 'package:nhancepolicy/logger.dart';
|
|
||||||
|
|
||||||
class CustomAppBar extends StatefulWidget implements PreferredSizeWidget {
|
|
||||||
@override
|
|
||||||
_CustomAppBarState createState() => _CustomAppBarState();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Size get preferredSize => Size.fromHeight(kToolbarHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CustomAppBarState extends State<CustomAppBar> {
|
|
||||||
bool showBackToHR = false;
|
|
||||||
late ApiService apiService;
|
|
||||||
final tokenService = TokenStorageService();
|
|
||||||
// 🔐 Secure storage instance
|
|
||||||
static const FlutterSecureStorage _secureStorage = FlutterSecureStorage();
|
|
||||||
// bool isLoading = true; // Add a loading state
|
|
||||||
// bool hideInactiveStatus = true;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
apiService = ApiService(context);
|
|
||||||
// _checkTokens();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> logout(BuildContext context) async {
|
|
||||||
// final prefs = await SharedPreferences.getInstance();
|
|
||||||
// final String? hrtoken = prefs.getString('_postToken');
|
|
||||||
// final String? token = prefs.getString('enrollToken');
|
|
||||||
// prefs.clear();
|
|
||||||
logDebug('LocalStorage Cleared');
|
|
||||||
apiService.logout();
|
|
||||||
// Navigator.pushNamed(context, 'hrLogin');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final sw = MediaQuery.of(context).size.width;
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Color(0xFFFFFCE5), // Set background color for AppBar
|
|
||||||
appBar: PreferredSize(
|
|
||||||
preferredSize: widget.preferredSize,
|
|
||||||
child: SafeArea(
|
|
||||||
child: Container(
|
|
||||||
// padding: Responsive.isDesktop(context)
|
|
||||||
// ? EdgeInsets.symmetric(horizontal: 46.0)
|
|
||||||
// : EdgeInsets.symmetric(horizontal: 0),
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: MediaQuery.of(context).size.width *
|
|
||||||
(Responsive.isDesktop(context) ? 0.03 : 0.02),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
// Logo Column
|
|
||||||
// Expanded(
|
|
||||||
// // flex: Responsive.isDesktop(context) ? 1 : 9,
|
|
||||||
// child:
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: Responsive.isDesktop(context)
|
|
||||||
? MainAxisAlignment.spaceEvenly
|
|
||||||
: MainAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.only(top: 10, bottom: 10),
|
|
||||||
width: 150,
|
|
||||||
height: 150,
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/nhance_client_logo.png',
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
// ),
|
|
||||||
// AdaptiveNavBar Column
|
|
||||||
|
|
||||||
// Expanded(
|
|
||||||
// // flex: Responsive.isDesktop(context) ? 1 : 9,
|
|
||||||
// child:
|
|
||||||
Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
NavBarItem(
|
|
||||||
text: "Change Branch",
|
|
||||||
onTap: () async {
|
|
||||||
await tokenService.clearBranchSession();
|
|
||||||
|
|
||||||
if (!mounted) return;
|
|
||||||
Navigator.pushNamedAndRemoveUntil(
|
|
||||||
context,
|
|
||||||
'branchSelection',
|
|
||||||
(route) => false,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
NavBarItem(
|
|
||||||
text: "Logout",
|
|
||||||
onTap: () => logout(context),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
// )
|
|
||||||
|
|
||||||
// Expanded(
|
|
||||||
// flex: Responsive.isDesktop(context) ? 9 : 3,
|
|
||||||
// child: AdaptiveNavBar(
|
|
||||||
// screenWidth: sw,
|
|
||||||
// backgroundColor: Color(0xFFFFFCE5),
|
|
||||||
// leading:
|
|
||||||
// Container(), // Set an empty container as we have the logo separately
|
|
||||||
// title: Text(''),
|
|
||||||
// navBarItems: [
|
|
||||||
// // if (Responsive.isDesktop(context))
|
|
||||||
// // if (hideInactiveStatus)
|
|
||||||
// // NavBarItem(
|
|
||||||
// // text: "Inactive Policy",
|
|
||||||
// // onTap: () {
|
|
||||||
// // Navigator.pushNamed(context, 'oldPolicy');
|
|
||||||
// // },
|
|
||||||
// // ),
|
|
||||||
// // if (showBackToHR)
|
|
||||||
// NavBarItem(
|
|
||||||
// text: "Change Branch",
|
|
||||||
// onTap: () async {
|
|
||||||
// final prefs = await SharedPreferences.getInstance();
|
|
||||||
// await prefs.remove('selected_branch');
|
|
||||||
// await prefs.remove('decoded_token');
|
|
||||||
// await prefs.remove('clientLogo');
|
|
||||||
// await prefs.remove('clientName');
|
|
||||||
// await prefs.remove('empAllowed_modules');
|
|
||||||
// await prefs.remove('empClientBranchId');
|
|
||||||
// await prefs.remove('empClientId');
|
|
||||||
// await prefs.remove('empEmail');
|
|
||||||
// await prefs.remove('empHrId');
|
|
||||||
// await prefs.remove('empPrimaryId');
|
|
||||||
// await prefs.remove('enrollmentAllowed_modules');
|
|
||||||
// await prefs.remove('enrollmentClient_id');
|
|
||||||
// await prefs.remove('enrollmentEmpClientBranchId');
|
|
||||||
// await prefs.remove('enrollmentEmpPrimaryId');
|
|
||||||
// await prefs.remove('enrollmentHrId');
|
|
||||||
// await prefs.remove('token');
|
|
||||||
// Navigator.pushNamed(context, 'branchSelection');
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// NavBarItem(
|
|
||||||
// text: "Logout",
|
|
||||||
// onTap: () async {
|
|
||||||
// logout(context);
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NavBarItem extends StatelessWidget {
|
|
||||||
final String text;
|
|
||||||
final VoidCallback? onTap;
|
|
||||||
|
|
||||||
const NavBarItem({
|
|
||||||
Key? key,
|
|
||||||
required this.text,
|
|
||||||
this.onTap,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return InkWell(
|
|
||||||
borderRadius: BorderRadius.circular(6),
|
|
||||||
onTap: onTap,
|
|
||||||
hoverColor: const Color(0xFFF4F3E7), // subtle hover background
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
|
||||||
child: Text(
|
|
||||||
text,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,88 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:adaptive_navbar/adaptive_navbar.dart';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
|
|
||||||
import '../responsive.dart';
|
|
||||||
|
|
||||||
class CustomAppBarChatbot extends StatelessWidget
|
|
||||||
implements PreferredSizeWidget {
|
|
||||||
@override
|
|
||||||
Size get preferredSize => Size.fromHeight(kToolbarHeight);
|
|
||||||
|
|
||||||
Future<void> logout(BuildContext context) async {
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
final String? token = prefs.getString('token');
|
|
||||||
|
|
||||||
if (token != null && token.isNotEmpty) {
|
|
||||||
await prefs.clear();
|
|
||||||
Navigator.pushNamed(context, 'phone');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final sw = MediaQuery.of(context).size.width;
|
|
||||||
final isPoliciesPage = Uri.base.fragment == 'policies';
|
|
||||||
return ClipRRect(
|
|
||||||
borderRadius: Responsive.isDesktop(context) || isPoliciesPage
|
|
||||||
? BorderRadius.zero
|
|
||||||
: BorderRadius.only(
|
|
||||||
bottomLeft: Radius.circular(32.0),
|
|
||||||
bottomRight: Radius.circular(32.0),
|
|
||||||
),
|
|
||||||
child: AppBar(
|
|
||||||
backgroundColor: const Color(0xFFFFFBDE),
|
|
||||||
elevation: 0, // Set background color for AppBar
|
|
||||||
toolbarHeight: kToolbarHeight,
|
|
||||||
titleSpacing: 0.0,
|
|
||||||
automaticallyImplyLeading: false,
|
|
||||||
title: Padding(
|
|
||||||
padding: Responsive.isDesktop(context)
|
|
||||||
? EdgeInsets.symmetric(horizontal: 16.0)
|
|
||||||
: EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
// Logo Column
|
|
||||||
Expanded(
|
|
||||||
flex: Responsive.isDesktop(context) ? 3 : 9,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: Responsive.isDesktop(context)
|
|
||||||
? MainAxisAlignment.spaceEvenly
|
|
||||||
: MainAxisAlignment
|
|
||||||
.start, // Adjust the alignment as needed
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.only(
|
|
||||||
top: 10, bottom: 10, left: 10, right: 10),
|
|
||||||
width: Responsive.isDesktop(context) ? 230 : 170,
|
|
||||||
height: Responsive.isDesktop(context) ? 230 : 170,
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/nhance_client_logo.png',
|
|
||||||
fit: BoxFit.contain, // Adjust the fit as needed
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// AdaptiveNavBar Column
|
|
||||||
|
|
||||||
Expanded(
|
|
||||||
flex: Responsive.isDesktop(context) ? 9 : 3,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
child: Icon(
|
|
||||||
Icons.close_outlined, // Replace with your desired icon
|
|
||||||
size: 24.0, // Adjust the icon size
|
|
||||||
color: Colors.black, // Adjust the icon color
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class CustomFooter extends StatelessWidget {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
padding: EdgeInsets.all(8.0),
|
|
||||||
color: Color(0xFFFFFBDE), // Background color of the footer
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Copyright @ ${DateTime.now().year} Nhance',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: Colors.grey[600], // Text color
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,162 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:adaptive_navbar/adaptive_navbar.dart';
|
|
||||||
import 'package:nhancepolicy/customAppBar/toastHelper.dart';
|
|
||||||
import 'package:nhancepolicy/responsive.dart';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:nhancepolicy/responsive.dart';
|
|
||||||
import 'package:nhancepolicy/logger.dart';
|
|
||||||
|
|
||||||
class CustomAppBar extends StatefulWidget implements PreferredSizeWidget {
|
|
||||||
@override
|
|
||||||
_CustomAppBarState createState() => _CustomAppBarState();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Size get preferredSize => Size.fromHeight(kToolbarHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CustomAppBarState extends State<CustomAppBar> {
|
|
||||||
bool showBackToHR = false;
|
|
||||||
// bool isLoading = true; // Add a loading state
|
|
||||||
// bool hideInactiveStatus = true;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_checkTokens();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _checkTokens() async {
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
// final String? hrtoken = prefs.getString('hrtoken');
|
|
||||||
final String? hrtoken = prefs.getString('enrollToken');
|
|
||||||
final String? token = prefs.getString('token');
|
|
||||||
// showBackToHR = prefs.getBool('showBackToHR')!;
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
showBackToHR = prefs.getBool('showBackToHR')!;
|
|
||||||
// showBackToHR = (hrtoken != null && hrtoken.isNotEmpty) &&
|
|
||||||
// (token != null && token.isNotEmpty);
|
|
||||||
// logDebug((hrtoken != null && hrtoken.isNotEmpty));
|
|
||||||
// logDebug((token != null && token.isNotEmpty));
|
|
||||||
// logDebug('showBackToHR $showBackToHR');
|
|
||||||
|
|
||||||
// hideInactiveStatus = token != null && token.isNotEmpty;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> hrLogout(BuildContext context) async {
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
final String? hrtoken = prefs.getString('hrtoken');
|
|
||||||
|
|
||||||
if (hrtoken != null && hrtoken.isNotEmpty) {
|
|
||||||
prefs.setBool('showBackToHR', false);
|
|
||||||
await prefs.remove('hrSelectArgumentsData');
|
|
||||||
prefs.remove('token');
|
|
||||||
Navigator.pushNamed(context, 'policies');
|
|
||||||
} else {
|
|
||||||
await prefs.clear();
|
|
||||||
Navigator.pushNamed(context, 'phone');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> logout(BuildContext context) async {
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
final String? hrtoken = prefs.getString('hrtoken');
|
|
||||||
final String? token = prefs.getString('token');
|
|
||||||
|
|
||||||
if (token != null && token.isNotEmpty) {
|
|
||||||
if (hrtoken != null && hrtoken.isNotEmpty) {
|
|
||||||
await prefs.clear();
|
|
||||||
Navigator.pushNamed(context, 'hrLogin');
|
|
||||||
} else {
|
|
||||||
await prefs.clear();
|
|
||||||
Navigator.pushNamed(context, 'phone');
|
|
||||||
}
|
|
||||||
} else if (hrtoken != null && hrtoken.isNotEmpty) {
|
|
||||||
await prefs.clear();
|
|
||||||
Navigator.pushNamed(context, 'hrLogin');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final sw = MediaQuery.of(context).size.width;
|
|
||||||
// if (isLoading) {
|
|
||||||
// // Show a loading indicator or a placeholder AppBar while waiting
|
|
||||||
// return AppBar(
|
|
||||||
// title: Text('Loading...'),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Color(0xFFFFFCE5), // Set background color for AppBar
|
|
||||||
appBar: PreferredSize(
|
|
||||||
preferredSize: widget.preferredSize,
|
|
||||||
child: SafeArea(
|
|
||||||
child: Container(
|
|
||||||
padding: Responsive.isDesktop(context)
|
|
||||||
? EdgeInsets.symmetric(horizontal: 16.0)
|
|
||||||
: EdgeInsets.symmetric(horizontal: 0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
// Logo Column
|
|
||||||
Expanded(
|
|
||||||
flex: Responsive.isDesktop(context) ? 3 : 9,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: Responsive.isDesktop(context)
|
|
||||||
? MainAxisAlignment.spaceEvenly
|
|
||||||
: MainAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.only(top: 10, bottom: 10),
|
|
||||||
width: 150,
|
|
||||||
height: 150,
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/nhance_client_logo.png',
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// AdaptiveNavBar Column
|
|
||||||
Expanded(
|
|
||||||
flex: Responsive.isDesktop(context) ? 9 : 3,
|
|
||||||
child: AdaptiveNavBar(
|
|
||||||
screenWidth: sw,
|
|
||||||
backgroundColor: Color(0xFFFFFCE5),
|
|
||||||
leading:
|
|
||||||
Container(), // Set an empty container as we have the logo separately
|
|
||||||
title: Text(''),
|
|
||||||
navBarItems: [
|
|
||||||
// if (Responsive.isDesktop(context))
|
|
||||||
// if (hideInactiveStatus)
|
|
||||||
// NavBarItem(
|
|
||||||
// text: "Inactive Policy",
|
|
||||||
// onTap: () {
|
|
||||||
// Navigator.pushNamed(context, 'oldPolicy');
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
if (showBackToHR)
|
|
||||||
NavBarItem(
|
|
||||||
text: "Back To HR",
|
|
||||||
onTap: () {
|
|
||||||
hrLogout(context);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
NavBarItem(
|
|
||||||
text: "Logout",
|
|
||||||
onTap: () async {
|
|
||||||
logout(context);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,807 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/svg.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'package:nhancepolicy/service/api_service.dart';
|
|
||||||
import 'package:nhancepolicy/service/svg_service.dart';
|
|
||||||
import 'package:nhancepolicy/service/token_storage_service.dart';
|
|
||||||
import 'package:nhancepolicy/logger.dart';
|
|
||||||
|
|
||||||
class NhanceSideBar extends StatefulWidget {
|
|
||||||
const NhanceSideBar({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<NhanceSideBar> createState() => _NhanceSideBarState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NhanceSideBarState extends State<NhanceSideBar> {
|
|
||||||
String? activeRoute;
|
|
||||||
late ApiService apiService;
|
|
||||||
OverlayEntry? _claimsOverlayEntry;
|
|
||||||
bool _isHoveringClaimsItem = false;
|
|
||||||
bool _isHoveringClaimsMenu = false;
|
|
||||||
DateTime? _claimsMenuSuppressUntil;
|
|
||||||
// bool isLoading = true; // Add a loading state
|
|
||||||
// bool hideInactiveStatus = true;
|
|
||||||
final tokenService = TokenStorageService();
|
|
||||||
// dynamic enrollmentModules = [];
|
|
||||||
// dynamic postModules = [];
|
|
||||||
|
|
||||||
List<Map<String, dynamic>> sideMenuItems = [];
|
|
||||||
List<int> postModules = [];
|
|
||||||
List<int> enrollmentModules = [];
|
|
||||||
List<String> claimsSubMenu = [];
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
apiService = ApiService(context);
|
|
||||||
_buildSideMenu();
|
|
||||||
// _checkTokens();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _buildSideMenu() async {
|
|
||||||
final enrollmentRaw =
|
|
||||||
await tokenService.readValue('enrollmentAllowed_modules'); // "[1]"
|
|
||||||
final postRaw =
|
|
||||||
await tokenService.readValue('empAllowed_modules'); // "[2,3,4]"
|
|
||||||
|
|
||||||
logDebug('enrollmentRaw $enrollmentRaw');
|
|
||||||
logDebug('postRaw $postRaw');
|
|
||||||
|
|
||||||
// ✅ Decode safely
|
|
||||||
enrollmentModules = enrollmentRaw != null && enrollmentRaw.isNotEmpty
|
|
||||||
? List<int>.from(jsonDecode(enrollmentRaw))
|
|
||||||
: [];
|
|
||||||
|
|
||||||
postModules = postRaw != null && postRaw.isNotEmpty
|
|
||||||
? List<int>.from(jsonDecode(postRaw))
|
|
||||||
: [];
|
|
||||||
|
|
||||||
final claimsSubMenuRaw = await tokenService.readValue('claims_sub_menu');
|
|
||||||
if (claimsSubMenuRaw != null && claimsSubMenuRaw.isNotEmpty) {
|
|
||||||
claimsSubMenu = List<dynamic>.from(jsonDecode(claimsSubMenuRaw))
|
|
||||||
.map((e) => e.toString())
|
|
||||||
.toList();
|
|
||||||
} else {
|
|
||||||
// Fallback: decoded JWT / selected branch may still have the list.
|
|
||||||
final decoded = tokenService.getDecodedToken();
|
|
||||||
final branch = tokenService.getSelectedBranch();
|
|
||||||
final fromDecoded = decoded?['claims_sub_menu'];
|
|
||||||
final fromBranch = branch?['claims_sub_menu'];
|
|
||||||
final source = fromBranch is List
|
|
||||||
? fromBranch
|
|
||||||
: (fromDecoded is List ? fromDecoded : const []);
|
|
||||||
claimsSubMenu = source.map((e) => e.toString()).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
logDebug('enrollmentModules $enrollmentModules');
|
|
||||||
logDebug('postModules $postModules');
|
|
||||||
logDebug('claimsSubMenu $claimsSubMenu');
|
|
||||||
|
|
||||||
final List<Map<String, dynamic>> items = [];
|
|
||||||
|
|
||||||
// ✅ POLICIES (1 OR 2)
|
|
||||||
if (enrollmentModules.contains(1) || postModules.contains(2)) {
|
|
||||||
items.add({
|
|
||||||
'route': 'policies',
|
|
||||||
'label': 'Policies',
|
|
||||||
'icon': 'policies',
|
|
||||||
});
|
|
||||||
items.add({
|
|
||||||
'route': 'pendingDependentApproval',
|
|
||||||
'label': 'Approvals',
|
|
||||||
'icon': 'pending_approve',
|
|
||||||
'useMaterialIcon': true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// CD
|
|
||||||
if (postModules.contains(3)) {
|
|
||||||
items.add({
|
|
||||||
'route': 'CdPoliciesList',
|
|
||||||
'label': 'CD',
|
|
||||||
'icon': 'cd',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// CLAIMS — only when module 4 and at least one sub-menu is allowed
|
|
||||||
if (postModules.contains(4) && _claimsSubMenuRoutes.isNotEmpty) {
|
|
||||||
items.add({
|
|
||||||
'route': 'ClaimsPolicies',
|
|
||||||
'label': 'Claims',
|
|
||||||
'icon': 'claims',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
sideMenuItems = items;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allowed Claims destinations based on claims_sub_menu.
|
|
||||||
List<({String label, String route})> get _claimsSubMenuRoutes {
|
|
||||||
final items = <({String label, String route})>[];
|
|
||||||
if (claimsSubMenu.contains('EB')) {
|
|
||||||
items.add((label: 'Employee Benefits', route: 'ClaimsPolicies'));
|
|
||||||
}
|
|
||||||
if (claimsSubMenu.contains('Non-EB')) {
|
|
||||||
items.add((label: 'Non-Employee Benefits', route: 'nonEBClaimsList'));
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get _hasMultipleClaimsSubMenus => _claimsSubMenuRoutes.length > 1;
|
|
||||||
|
|
||||||
String? get _singleClaimsRoute {
|
|
||||||
final routes = _claimsSubMenuRoutes;
|
|
||||||
if (routes.length == 1) return routes.first.route;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> logout(BuildContext context) async {
|
|
||||||
// final prefs = await SharedPreferences.getInstance();
|
|
||||||
// final String? hrtoken = prefs.getString('_postToken');
|
|
||||||
// final String? token = prefs.getString('enrollToken');
|
|
||||||
// prefs.clear();
|
|
||||||
logDebug('LocalStorage Cleared');
|
|
||||||
apiService.logout();
|
|
||||||
// Navigator.pushNamed(context, 'hrLogin');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeDependencies() {
|
|
||||||
super.didChangeDependencies();
|
|
||||||
// Safely capture the current route name
|
|
||||||
final newRoute = ModalRoute.of(context)?.settings.name;
|
|
||||||
if (activeRoute != newRoute) {
|
|
||||||
setState(() {
|
|
||||||
activeRoute = newRoute;
|
|
||||||
});
|
|
||||||
_closeClaimsMenu();
|
|
||||||
// Re-verify the menu items if the route changes
|
|
||||||
_buildSideMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _navigate(String routeName) {
|
|
||||||
_closeClaimsMenu();
|
|
||||||
if (activeRoute == routeName) return;
|
|
||||||
// Navigation should happen first, didChangeDependencies will handle the state
|
|
||||||
Navigator.pushReplacementNamed(context, routeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showClaimsSubMenu(GlobalKey key) {
|
|
||||||
if (!_hasMultipleClaimsSubMenus) return;
|
|
||||||
|
|
||||||
if (_claimsMenuSuppressUntil != null &&
|
|
||||||
DateTime.now().isBefore(_claimsMenuSuppressUntil!)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final targetContext = key.currentContext;
|
|
||||||
if (targetContext == null) return;
|
|
||||||
|
|
||||||
final box = targetContext.findRenderObject() as RenderBox?;
|
|
||||||
if (box == null) return;
|
|
||||||
|
|
||||||
final offset = box.localToGlobal(Offset.zero);
|
|
||||||
final subMenus = _claimsSubMenuRoutes;
|
|
||||||
_claimsOverlayEntry?.remove();
|
|
||||||
_claimsOverlayEntry = OverlayEntry(
|
|
||||||
builder: (context) => Positioned(
|
|
||||||
left: offset.dx + box.size.width + 6,
|
|
||||||
top: offset.dy,
|
|
||||||
child: MouseRegion(
|
|
||||||
onEnter: (_) {
|
|
||||||
_isHoveringClaimsMenu = true;
|
|
||||||
},
|
|
||||||
onExit: (_) {
|
|
||||||
_isHoveringClaimsMenu = false;
|
|
||||||
_scheduleCloseClaimsMenu();
|
|
||||||
},
|
|
||||||
child: Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: Container(
|
|
||||||
width: 200,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 6),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
boxShadow: const [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black26,
|
|
||||||
blurRadius: 8,
|
|
||||||
offset: Offset(0, 2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
for (final item in subMenus)
|
|
||||||
_buildClaimsMenuItem(item.label, item.route),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
Overlay.of(context).insert(_claimsOverlayEntry!);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildClaimsMenuItem(String label, String route) {
|
|
||||||
return _ClaimsSubMenuItem(
|
|
||||||
label: label,
|
|
||||||
onTap: () {
|
|
||||||
_claimsMenuSuppressUntil =
|
|
||||||
DateTime.now().add(const Duration(milliseconds: 700));
|
|
||||||
_isHoveringClaimsItem = false;
|
|
||||||
_isHoveringClaimsMenu = false;
|
|
||||||
_closeClaimsMenu();
|
|
||||||
_navigate(route);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _scheduleCloseClaimsMenu() {
|
|
||||||
Future.delayed(const Duration(milliseconds: 120), () {
|
|
||||||
if (!_isHoveringClaimsItem && !_isHoveringClaimsMenu) {
|
|
||||||
_closeClaimsMenu();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _closeClaimsMenu() {
|
|
||||||
_claimsOverlayEntry?.remove();
|
|
||||||
_claimsOverlayEntry = null;
|
|
||||||
_isHoveringClaimsMenu = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _openHelpDialog() async {
|
|
||||||
final isPostUser = postModules.isNotEmpty;
|
|
||||||
|
|
||||||
await showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
builder: (dialogContext) => _HelpContactDialog(
|
|
||||||
isPostUser: isPostUser,
|
|
||||||
apiService: apiService,
|
|
||||||
tokenService: tokenService,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
width: 70,
|
|
||||||
color: const Color(0xFF009E9E),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
// const SizedBox(height: 12),
|
|
||||||
|
|
||||||
//
|
|
||||||
// _SideItem(
|
|
||||||
// // icon: Icons.policy,
|
|
||||||
// icon: SvgPicture.string(
|
|
||||||
// SvgService.getSvg('policies'),
|
|
||||||
// width: 35,
|
|
||||||
// height: 35,
|
|
||||||
// colorFilter: const ColorFilter.mode(
|
|
||||||
// Colors.white,
|
|
||||||
// BlendMode.srcIn,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// label: "Policies",
|
|
||||||
// isActive: activeRoute == 'oldPolicy', // or correct policy route
|
|
||||||
// onTap: () => _navigate('oldPolicy'),
|
|
||||||
// ),
|
|
||||||
//
|
|
||||||
// _SideItem(
|
|
||||||
// // icon: Icons.credit_card,
|
|
||||||
// icon: SvgPicture.string(
|
|
||||||
// SvgService.getSvg('cd'),
|
|
||||||
// width: 35,
|
|
||||||
// height: 35,
|
|
||||||
// colorFilter: const ColorFilter.mode(
|
|
||||||
// Colors.white,
|
|
||||||
// BlendMode.srcIn,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// label: "CD",
|
|
||||||
// isActive: activeRoute == 'cdTransactionDetails',
|
|
||||||
// onTap: () => _navigate('cdTransactionDetails'),
|
|
||||||
// ),
|
|
||||||
//
|
|
||||||
// _SideItem(
|
|
||||||
// // icon: Icons.assignment,
|
|
||||||
// icon: SvgPicture.string(
|
|
||||||
// SvgService.getSvg('claims'),
|
|
||||||
// width: 35,
|
|
||||||
// height: 35,
|
|
||||||
// colorFilter: const ColorFilter.mode(
|
|
||||||
// Colors.white,
|
|
||||||
// BlendMode.srcIn,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// label: "Claims",
|
|
||||||
// isActive: activeRoute == 'claims', // claims tab inside dashboard
|
|
||||||
// onTap: () => _navigate('claims'),
|
|
||||||
// ),
|
|
||||||
//
|
|
||||||
// const Spacer(),
|
|
||||||
//
|
|
||||||
// _SideItem(
|
|
||||||
// // icon: Icons.logout,
|
|
||||||
// icon: SvgPicture.string(
|
|
||||||
// SvgService.getSvg('logout'),
|
|
||||||
// width: 35,
|
|
||||||
// height: 35,
|
|
||||||
// colorFilter: const ColorFilter.mode(
|
|
||||||
// Colors.white,
|
|
||||||
// BlendMode.srcIn,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// label: "Logout",
|
|
||||||
// onTap: () {
|
|
||||||
// logout(context);
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
if (postModules.isNotEmpty && postModules.contains(5))
|
|
||||||
_SideItem(
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.space_dashboard_outlined,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 30,
|
|
||||||
),
|
|
||||||
label: "Dashboard",
|
|
||||||
isActive: activeRoute == 'claimsOverviewDashboard',
|
|
||||||
onTap: () => _navigate('claimsOverviewDashboard'),
|
|
||||||
),
|
|
||||||
|
|
||||||
...sideMenuItems.map((item) {
|
|
||||||
final isClaims = item['route'] == 'ClaimsPolicies';
|
|
||||||
final isClaimsActive =
|
|
||||||
activeRoute == 'ClaimsPolicies' || activeRoute == 'nonEBClaimsList';
|
|
||||||
final itemKey = GlobalKey();
|
|
||||||
final showClaimsSubMenu = isClaims && _hasMultipleClaimsSubMenus;
|
|
||||||
final claimsDirectRoute =
|
|
||||||
isClaims ? (_singleClaimsRoute ?? 'ClaimsPolicies') : null;
|
|
||||||
final useMaterialIcon = item['useMaterialIcon'] == true;
|
|
||||||
|
|
||||||
return _SideItem(
|
|
||||||
key: itemKey,
|
|
||||||
icon: useMaterialIcon
|
|
||||||
? const Icon(
|
|
||||||
Icons.how_to_reg_outlined,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 30,
|
|
||||||
)
|
|
||||||
: SvgPicture.string(
|
|
||||||
SvgService.getSvg(item['icon']),
|
|
||||||
width: 35,
|
|
||||||
height: 35,
|
|
||||||
colorFilter: const ColorFilter.mode(
|
|
||||||
Colors.white,
|
|
||||||
BlendMode.srcIn,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
label: item['label'],
|
|
||||||
isActive: isClaims ? isClaimsActive : activeRoute == item['route'],
|
|
||||||
onTap: showClaimsSubMenu
|
|
||||||
? null
|
|
||||||
: () => _navigate(
|
|
||||||
claimsDirectRoute ?? item['route'] as String,
|
|
||||||
),
|
|
||||||
onHoverEnter: showClaimsSubMenu
|
|
||||||
? () {
|
|
||||||
_isHoveringClaimsItem = true;
|
|
||||||
_showClaimsSubMenu(itemKey);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
onHoverExit: showClaimsSubMenu
|
|
||||||
? () {
|
|
||||||
_isHoveringClaimsItem = false;
|
|
||||||
_scheduleCloseClaimsMenu();
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// if (postModules.isNotEmpty && postModules.contains(5))
|
|
||||||
// _SideItem(
|
|
||||||
// // icon: Icons.dashboard,
|
|
||||||
// icon: SvgPicture.string(
|
|
||||||
// SvgService.getSvg('dashboard'),
|
|
||||||
// width: 35,
|
|
||||||
// height: 35,
|
|
||||||
// colorFilter: const ColorFilter.mode(
|
|
||||||
// Colors.white,
|
|
||||||
// BlendMode.srcIn,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// label: "Insights",
|
|
||||||
// isActive: activeRoute == 'hrDashboard',
|
|
||||||
// onTap: () => _navigate('hrDashboard'),
|
|
||||||
// ),
|
|
||||||
|
|
||||||
const Spacer(),
|
|
||||||
|
|
||||||
if (enrollmentModules.isNotEmpty || postModules.isNotEmpty)
|
|
||||||
_SideItem(
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.help_outline,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 30,
|
|
||||||
),
|
|
||||||
label: 'Help',
|
|
||||||
onTap: _openHelpDialog,
|
|
||||||
),
|
|
||||||
|
|
||||||
_SideItem(
|
|
||||||
icon: SvgPicture.string(
|
|
||||||
SvgService.getSvg('logout'),
|
|
||||||
width: 35,
|
|
||||||
height: 35,
|
|
||||||
colorFilter: const ColorFilter.mode(
|
|
||||||
Colors.white,
|
|
||||||
BlendMode.srcIn,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
label: "Logout",
|
|
||||||
onTap: () => logout(context),
|
|
||||||
),
|
|
||||||
|
|
||||||
// const SizedBox(height: 10),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _HelpContactDialog extends StatefulWidget {
|
|
||||||
final bool isPostUser;
|
|
||||||
final ApiService apiService;
|
|
||||||
final TokenStorageService tokenService;
|
|
||||||
|
|
||||||
const _HelpContactDialog({
|
|
||||||
required this.isPostUser,
|
|
||||||
required this.apiService,
|
|
||||||
required this.tokenService,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<_HelpContactDialog> createState() => _HelpContactDialogState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _HelpContactDialogState extends State<_HelpContactDialog> {
|
|
||||||
bool _isLoading = false;
|
|
||||||
List<Map<String, dynamic>> _level1Contacts = [];
|
|
||||||
List<Map<String, dynamic>> _level2Contacts = [];
|
|
||||||
String? _errorMessage;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
if (widget.isPostUser) {
|
|
||||||
_loadClientRM();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _loadClientRM() async {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = true;
|
|
||||||
_errorMessage = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
final clientId = await widget.tokenService.readValue('empClientId');
|
|
||||||
final token = widget.tokenService.getCurrentToken();
|
|
||||||
|
|
||||||
if (clientId == null ||
|
|
||||||
clientId.toString().isEmpty ||
|
|
||||||
token == null ||
|
|
||||||
token.isEmpty) {
|
|
||||||
setState(() {
|
|
||||||
_errorMessage = 'Client details not found';
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final response = await widget.apiService.getClientRMApi(
|
|
||||||
clientId.toString(),
|
|
||||||
token,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!mounted) return;
|
|
||||||
|
|
||||||
if (response['status'] == 'success' && response['data'] is Map) {
|
|
||||||
final data = Map<String, dynamic>.from(response['data'] as Map);
|
|
||||||
setState(() {
|
|
||||||
_level1Contacts = _parseContacts(data['level_1']);
|
|
||||||
_level2Contacts = _parseContacts(data['level_2']);
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setState(() {
|
|
||||||
_errorMessage =
|
|
||||||
response['message']?.toString() ?? 'Failed to load contacts';
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logDebug('getClientRM failed: $e');
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {
|
|
||||||
_errorMessage = 'Failed to load contacts';
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Map<String, dynamic>> _parseContacts(dynamic raw) {
|
|
||||||
if (raw is! List) return [];
|
|
||||||
return raw
|
|
||||||
.whereType<Map>()
|
|
||||||
.map((item) => Map<String, dynamic>.from(item))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildContactCard(Map<String, dynamic> contact) {
|
|
||||||
final name = contact['first_name']?.toString() ?? '-';
|
|
||||||
final email = contact['email']?.toString() ?? '-';
|
|
||||||
final mobile = contact['mobile']?.toString() ?? '-';
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
width: double.infinity,
|
|
||||||
margin: const EdgeInsets.only(bottom: 10),
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFF5F5F5),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
border: Border.all(color: const Color(0xFFE8E8E8)),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
name,
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: const Color(0xFF009195),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 6),
|
|
||||||
Text(
|
|
||||||
email,
|
|
||||||
style: GoogleFonts.poppins(fontSize: 13, color: Colors.black87),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 2),
|
|
||||||
Text(
|
|
||||||
mobile,
|
|
||||||
style: GoogleFonts.poppins(fontSize: 13, color: Colors.black87),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildLevelSection({
|
|
||||||
required String title,
|
|
||||||
required List<Map<String, dynamic>> contacts,
|
|
||||||
}) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
if (contacts.isEmpty)
|
|
||||||
Text(
|
|
||||||
'No contact assigned',
|
|
||||||
style: GoogleFonts.poppins(fontSize: 13, color: Colors.black54),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
...contacts.map(_buildContactCard),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildContent() {
|
|
||||||
if (!widget.isPostUser) {
|
|
||||||
return Center(
|
|
||||||
child: Text(
|
|
||||||
'No Level Contact',
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.black54,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_isLoading) {
|
|
||||||
return const SizedBox(
|
|
||||||
height: 180,
|
|
||||||
child: Center(child: CircularProgressIndicator()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_errorMessage != null) {
|
|
||||||
return Center(
|
|
||||||
child: Text(
|
|
||||||
_errorMessage!,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: GoogleFonts.poppins(fontSize: 14, color: Colors.red),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
_buildLevelSection(
|
|
||||||
title: 'Level 1 - Account Manager',
|
|
||||||
contacts: _level1Contacts,
|
|
||||||
),
|
|
||||||
_buildLevelSection(
|
|
||||||
title: 'Level 2 - Head',
|
|
||||||
contacts: _level2Contacts,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final maxHeight = MediaQuery.of(context).size.height * 0.65;
|
|
||||||
final cappedMaxHeight = maxHeight.clamp(280.0, 520.0);
|
|
||||||
|
|
||||||
return AlertDialog(
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
|
||||||
title: Text(
|
|
||||||
'Help',
|
|
||||||
style: GoogleFonts.poppins(fontWeight: FontWeight.w600, fontSize: 18),
|
|
||||||
),
|
|
||||||
content: SizedBox(
|
|
||||||
width: 560,
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(maxHeight: cappedMaxHeight),
|
|
||||||
child: Scrollbar(
|
|
||||||
child: SingleChildScrollView(child: _buildContent()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: const Color(0xFF009195),
|
|
||||||
),
|
|
||||||
child: Text('Close', style: GoogleFonts.poppins(color: Colors.white)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SideItem extends StatelessWidget {
|
|
||||||
// final IconData icon;
|
|
||||||
final Widget icon; // 👈 changed
|
|
||||||
final String label;
|
|
||||||
final bool isActive;
|
|
||||||
final VoidCallback? onTap;
|
|
||||||
final VoidCallback? onHoverEnter;
|
|
||||||
final VoidCallback? onHoverExit;
|
|
||||||
|
|
||||||
const _SideItem({
|
|
||||||
super.key,
|
|
||||||
required this.icon,
|
|
||||||
required this.label,
|
|
||||||
this.isActive = false,
|
|
||||||
this.onTap,
|
|
||||||
this.onHoverEnter,
|
|
||||||
this.onHoverExit,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return MouseRegion(
|
|
||||||
onEnter: (_) => onHoverEnter?.call(),
|
|
||||||
onExit: (_) => onHoverExit?.call(),
|
|
||||||
child: InkWell(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
margin: const EdgeInsets.symmetric(vertical: 6),
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: isActive
|
|
||||||
? const Color(0xFF065D61) // ✅ ACTIVE like your screenshot
|
|
||||||
: Colors.transparent,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
// 👇 SVG or Icon widget
|
|
||||||
icon,
|
|
||||||
// Icon(icon, color: Colors.white, size: 22),
|
|
||||||
const SizedBox(height: 6),
|
|
||||||
Text(
|
|
||||||
label,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
height: 1.15,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ClaimsSubMenuItem extends StatefulWidget {
|
|
||||||
final String label;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
const _ClaimsSubMenuItem({
|
|
||||||
required this.label,
|
|
||||||
required this.onTap,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<_ClaimsSubMenuItem> createState() => _ClaimsSubMenuItemState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ClaimsSubMenuItemState extends State<_ClaimsSubMenuItem> {
|
|
||||||
bool isHovered = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return MouseRegion(
|
|
||||||
onEnter: (_) => setState(() => isHovered = true),
|
|
||||||
onExit: (_) => setState(() => isHovered = false),
|
|
||||||
child: GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: widget.onTap,
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
color: isHovered ? const Color(0xFFE5F6F6) : Colors.white,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Text(
|
|
||||||
widget.label,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: isHovered ? const Color(0xFF006F73) : Colors.black87,
|
|
||||||
fontWeight: isHovered ? FontWeight.w600 : FontWeight.w400,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,286 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
// import 'package:fluttertoast/fluttertoast.dart';
|
|
||||||
import 'package:toastification/toastification.dart';
|
|
||||||
import 'package:nhancepolicy/logger.dart';
|
|
||||||
|
|
||||||
class ToastHelper {
|
|
||||||
static void showSuccessToast(BuildContext context, String message) {
|
|
||||||
toastification.show(
|
|
||||||
context: context,
|
|
||||||
type: ToastificationType.success,
|
|
||||||
style: ToastificationStyle.flatColored,
|
|
||||||
autoCloseDuration: const Duration(seconds: 2),
|
|
||||||
title: Text(
|
|
||||||
message,
|
|
||||||
maxLines: 3, // ✅ allow wrapping
|
|
||||||
overflow: TextOverflow.visible,
|
|
||||||
softWrap: true,
|
|
||||||
),
|
|
||||||
// you can also use RichText widget for title and description parameters
|
|
||||||
// description: RichText(
|
|
||||||
// text: const TextSpan(text: 'This is a sample toast message. ')),
|
|
||||||
alignment: Alignment.topRight,
|
|
||||||
direction: TextDirection.ltr,
|
|
||||||
animationDuration: const Duration(milliseconds: 100),
|
|
||||||
animationBuilder: (context, animation, alignment, child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: animation,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.check),
|
|
||||||
primaryColor: Colors.green,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
foregroundColor: Colors.black,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: const [
|
|
||||||
BoxShadow(
|
|
||||||
color: Color(0x07000000),
|
|
||||||
blurRadius: 16,
|
|
||||||
offset: Offset(0, 16),
|
|
||||||
spreadRadius: 0,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
showProgressBar: true,
|
|
||||||
closeButtonShowType: CloseButtonShowType.onHover,
|
|
||||||
closeOnClick: false,
|
|
||||||
pauseOnHover: true,
|
|
||||||
dragToClose: true,
|
|
||||||
applyBlurEffect: true,
|
|
||||||
callbacks: ToastificationCallbacks(
|
|
||||||
onTap: (toastItem) => logDebug('Toast ${toastItem.id} tapped'),
|
|
||||||
onCloseButtonTap: (toastItem) =>
|
|
||||||
logDebug('Toast ${toastItem.id} close button tapped'),
|
|
||||||
onAutoCompleteCompleted: (toastItem) =>
|
|
||||||
logDebug('Toast ${toastItem.id} auto complete completed'),
|
|
||||||
onDismissed: (toastItem) => logDebug('Toast ${toastItem.id} dismissed'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void showWarningToast(BuildContext context, String message) {
|
|
||||||
toastification.show(
|
|
||||||
context: context,
|
|
||||||
type: ToastificationType.warning,
|
|
||||||
style: ToastificationStyle.flatColored,
|
|
||||||
autoCloseDuration: const Duration(seconds: 2),
|
|
||||||
title: Text(message),
|
|
||||||
// you can also use RichText widget for title and description parameters
|
|
||||||
// description: RichText(
|
|
||||||
// text: const TextSpan(text: 'This is a sample toast message. ')),
|
|
||||||
alignment: Alignment.topRight,
|
|
||||||
direction: TextDirection.ltr,
|
|
||||||
animationDuration: const Duration(milliseconds: 100),
|
|
||||||
animationBuilder: (context, animation, alignment, child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: animation,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.warning),
|
|
||||||
primaryColor: Colors.amberAccent,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
foregroundColor: Colors.black,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: const [
|
|
||||||
BoxShadow(
|
|
||||||
color: Color(0x07000000),
|
|
||||||
blurRadius: 16,
|
|
||||||
offset: Offset(0, 16),
|
|
||||||
spreadRadius: 0,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
showProgressBar: true,
|
|
||||||
closeButtonShowType: CloseButtonShowType.onHover,
|
|
||||||
closeOnClick: false,
|
|
||||||
pauseOnHover: true,
|
|
||||||
dragToClose: true,
|
|
||||||
applyBlurEffect: true,
|
|
||||||
callbacks: ToastificationCallbacks(
|
|
||||||
onTap: (toastItem) => logDebug('Toast ${toastItem.id} tapped'),
|
|
||||||
onCloseButtonTap: (toastItem) =>
|
|
||||||
logDebug('Toast ${toastItem.id} close button tapped'),
|
|
||||||
onAutoCompleteCompleted: (toastItem) =>
|
|
||||||
logDebug('Toast ${toastItem.id} auto complete completed'),
|
|
||||||
onDismissed: (toastItem) => logDebug('Toast ${toastItem.id} dismissed'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void showErrorToast(BuildContext context, String message) {
|
|
||||||
toastification.show(
|
|
||||||
context: context,
|
|
||||||
type: ToastificationType.error,
|
|
||||||
style: ToastificationStyle.flatColored,
|
|
||||||
autoCloseDuration: const Duration(seconds: 2),
|
|
||||||
title: Text(
|
|
||||||
message,
|
|
||||||
maxLines: 3, // ✅ allow wrapping
|
|
||||||
overflow: TextOverflow.visible,
|
|
||||||
softWrap: true,
|
|
||||||
),
|
|
||||||
// you can also use RichText widget for title and description parameters
|
|
||||||
// description: RichText(
|
|
||||||
// text: const TextSpan(text: 'This is a sample toast message. ')),
|
|
||||||
alignment: Alignment.topRight,
|
|
||||||
direction: TextDirection.ltr,
|
|
||||||
animationDuration: const Duration(milliseconds: 100),
|
|
||||||
animationBuilder: (context, animation, alignment, child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: animation,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.error),
|
|
||||||
primaryColor: Colors.redAccent,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
foregroundColor: Colors.black,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: const [
|
|
||||||
BoxShadow(
|
|
||||||
color: Color(0x07000000),
|
|
||||||
blurRadius: 16,
|
|
||||||
offset: Offset(0, 16),
|
|
||||||
spreadRadius: 0,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
showProgressBar: true,
|
|
||||||
closeButtonShowType: CloseButtonShowType.onHover,
|
|
||||||
closeOnClick: false,
|
|
||||||
pauseOnHover: true,
|
|
||||||
dragToClose: true,
|
|
||||||
applyBlurEffect: true,
|
|
||||||
callbacks: ToastificationCallbacks(
|
|
||||||
onTap: (toastItem) => logDebug('Toast ${toastItem.id} tapped'),
|
|
||||||
onCloseButtonTap: (toastItem) =>
|
|
||||||
logDebug('Toast ${toastItem.id} close button tapped'),
|
|
||||||
onAutoCompleteCompleted: (toastItem) =>
|
|
||||||
logDebug('Toast ${toastItem.id} auto complete completed'),
|
|
||||||
onDismissed: (toastItem) => logDebug('Toast ${toastItem.id} dismissed'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
// _showToast(context, message, Colors.red);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void showErrorToast2(
|
|
||||||
BuildContext context, String message, String decmessage) {
|
|
||||||
toastification.show(
|
|
||||||
context: context,
|
|
||||||
type: ToastificationType.error,
|
|
||||||
style: ToastificationStyle.flatColored,
|
|
||||||
autoCloseDuration: const Duration(seconds: 2),
|
|
||||||
title: Text(message),
|
|
||||||
// you can also use RichText widget for title and description parameters
|
|
||||||
description: RichText(text: TextSpan(text: decmessage)),
|
|
||||||
alignment: Alignment.topRight,
|
|
||||||
direction: TextDirection.ltr,
|
|
||||||
animationDuration: const Duration(milliseconds: 100),
|
|
||||||
animationBuilder: (context, animation, alignment, child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: animation,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.error),
|
|
||||||
primaryColor: Colors.redAccent,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
foregroundColor: Colors.black,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: const [
|
|
||||||
BoxShadow(
|
|
||||||
color: Color(0x07000000),
|
|
||||||
blurRadius: 16,
|
|
||||||
offset: Offset(0, 16),
|
|
||||||
spreadRadius: 0,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
showProgressBar: true,
|
|
||||||
closeButtonShowType: CloseButtonShowType.onHover,
|
|
||||||
closeOnClick: false,
|
|
||||||
pauseOnHover: true,
|
|
||||||
dragToClose: true,
|
|
||||||
applyBlurEffect: true,
|
|
||||||
callbacks: ToastificationCallbacks(
|
|
||||||
onTap: (toastItem) => logDebug('Toast ${toastItem.id} tapped'),
|
|
||||||
onCloseButtonTap: (toastItem) =>
|
|
||||||
logDebug('Toast ${toastItem.id} close button tapped'),
|
|
||||||
onAutoCompleteCompleted: (toastItem) =>
|
|
||||||
logDebug('Toast ${toastItem.id} auto complete completed'),
|
|
||||||
onDismissed: (toastItem) => logDebug('Toast ${toastItem.id} dismissed'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
// _showToast(context, message, Colors.red);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void showInfoToast(BuildContext context, String message) {
|
|
||||||
toastification.show(
|
|
||||||
context: context,
|
|
||||||
type: ToastificationType.info,
|
|
||||||
style: ToastificationStyle.flatColored,
|
|
||||||
autoCloseDuration: const Duration(seconds: 2),
|
|
||||||
title: Text(message),
|
|
||||||
// you can also use RichText widget for title and description parameters
|
|
||||||
// description: RichText(
|
|
||||||
// text: const TextSpan(text: 'This is a sample toast message. ')),
|
|
||||||
alignment: Alignment.topRight,
|
|
||||||
direction: TextDirection.ltr,
|
|
||||||
animationDuration: const Duration(milliseconds: 100),
|
|
||||||
animationBuilder: (context, animation, alignment, child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: animation,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.info),
|
|
||||||
primaryColor: Colors.lightBlue,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
foregroundColor: Colors.black,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: const [
|
|
||||||
BoxShadow(
|
|
||||||
color: Color(0x07000000),
|
|
||||||
blurRadius: 16,
|
|
||||||
offset: Offset(0, 16),
|
|
||||||
spreadRadius: 0,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
showProgressBar: true,
|
|
||||||
closeButtonShowType: CloseButtonShowType.onHover,
|
|
||||||
closeOnClick: false,
|
|
||||||
pauseOnHover: true,
|
|
||||||
dragToClose: true,
|
|
||||||
applyBlurEffect: true,
|
|
||||||
callbacks: ToastificationCallbacks(
|
|
||||||
onTap: (toastItem) => logDebug('Toast ${toastItem.id} tapped'),
|
|
||||||
onCloseButtonTap: (toastItem) =>
|
|
||||||
logDebug('Toast ${toastItem.id} close button tapped'),
|
|
||||||
onAutoCompleteCompleted: (toastItem) =>
|
|
||||||
logDebug('Toast ${toastItem.id} auto complete completed'),
|
|
||||||
onDismissed: (toastItem) => logDebug('Toast ${toastItem.id} dismissed'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
// _showToast(context, message, Colors.red);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _showToast(BuildContext context, String message, Color color) {
|
|
||||||
// Fluttertoast.showToast(
|
|
||||||
// msg: message,
|
|
||||||
// toastLength: Toast.LENGTH_SHORT,
|
|
||||||
// gravity: ToastGravity.TOP,
|
|
||||||
// timeInSecForIosWeb: 5,
|
|
||||||
// backgroundColor: color,
|
|
||||||
// textColor: Colors.white,
|
|
||||||
// fontSize: 16.0,
|
|
||||||
// );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,400 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
import '../config/environment.dart';
|
|
||||||
import '../service/token_storage_service.dart';
|
|
||||||
|
|
||||||
class NhanceTopBar extends StatefulWidget implements PreferredSizeWidget {
|
|
||||||
const NhanceTopBar({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Size get preferredSize => const Size.fromHeight(64);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<NhanceTopBar> createState() => _NhanceTopBarState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NhanceTopBarState extends State<NhanceTopBar> {
|
|
||||||
final tokenStorage = TokenStorageService();
|
|
||||||
|
|
||||||
List<Map<String, dynamic>> branches = [];
|
|
||||||
Map<String, dynamic>? selectedBranch;
|
|
||||||
|
|
||||||
/// client_id → logo URL
|
|
||||||
final Map<String, String> _logoByClientId = {};
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_loadBranches();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _loadBranches() {
|
|
||||||
branches = tokenStorage.getCombinedBranches();
|
|
||||||
selectedBranch = tokenStorage.getSelectedBranch();
|
|
||||||
setState(() {});
|
|
||||||
_loadBranchLogos();
|
|
||||||
}
|
|
||||||
|
|
||||||
String _clientKey(Map<String, dynamic> branch) {
|
|
||||||
return (branch['client_id'] ?? branch['id'] ?? '').toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
String? _logoFromBranch(Map<String, dynamic> branch) {
|
|
||||||
final candidates = [
|
|
||||||
branch['client_logo'],
|
|
||||||
branch['logo'],
|
|
||||||
branch['clientLogo'],
|
|
||||||
];
|
|
||||||
for (final value in candidates) {
|
|
||||||
final url = value?.toString().trim() ?? '';
|
|
||||||
if (url.isNotEmpty) return url;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic>? _decodeBranchToken(String token) {
|
|
||||||
try {
|
|
||||||
final parts = token.split('.');
|
|
||||||
if (parts.length != 3) return null;
|
|
||||||
|
|
||||||
final payload = base64Url.normalize(parts[1]);
|
|
||||||
final decoded = utf8.decode(base64Url.decode(payload));
|
|
||||||
final parsed = json.decode(decoded);
|
|
||||||
if (parsed is Map<String, dynamic>) return parsed;
|
|
||||||
if (parsed is Map) return Map<String, dynamic>.from(parsed);
|
|
||||||
return null;
|
|
||||||
} catch (_) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read post/pre client + branch ids from selected branch JWT payload.
|
|
||||||
Map<String, String>? _clientDetailsParamsFromBranch(
|
|
||||||
Map<String, dynamic> branch,
|
|
||||||
) {
|
|
||||||
final token = branch['token']?.toString() ??
|
|
||||||
tokenStorage.getCurrentToken() ??
|
|
||||||
'';
|
|
||||||
if (token.isEmpty) return null;
|
|
||||||
|
|
||||||
final decoded = _decodeBranchToken(token);
|
|
||||||
if (decoded == null) return null;
|
|
||||||
|
|
||||||
final postClientId = decoded['post_client_id']?.toString().trim() ?? '';
|
|
||||||
final postBranchId = decoded['post_branch_id']?.toString().trim() ?? '';
|
|
||||||
final preClientId = decoded['pre_client_id']?.toString().trim() ?? '';
|
|
||||||
final preBranchId = decoded['pre_branch_id']?.toString().trim() ?? '';
|
|
||||||
|
|
||||||
if (postClientId.isEmpty &&
|
|
||||||
postBranchId.isEmpty &&
|
|
||||||
preClientId.isEmpty &&
|
|
||||||
preBranchId.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
'post_client_id': postClientId,
|
|
||||||
'post_branch_id': postBranchId,
|
|
||||||
'pre_client_id': preClientId,
|
|
||||||
'pre_branch_id': preBranchId,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _loadBranchLogos() async {
|
|
||||||
final uniqueBranches = <String, Map<String, dynamic>>{};
|
|
||||||
for (final branch in branches) {
|
|
||||||
final key = _clientKey(branch);
|
|
||||||
if (key.isEmpty) continue;
|
|
||||||
uniqueBranches.putIfAbsent(key, () => branch);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final entry in uniqueBranches.entries) {
|
|
||||||
final key = entry.key;
|
|
||||||
if (_logoByClientId.containsKey(key)) continue;
|
|
||||||
|
|
||||||
final fromBranch = _logoFromBranch(entry.value);
|
|
||||||
if (fromBranch != null) {
|
|
||||||
if (!mounted) return;
|
|
||||||
setState(() => _logoByClientId[key] = fromBranch);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final logoUrl = await _fetchClientLogoUrl(entry.value);
|
|
||||||
if (!mounted) return;
|
|
||||||
if (logoUrl != null && logoUrl.isNotEmpty) {
|
|
||||||
setState(() => _logoByClientId[key] = logoUrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String?> _fetchClientLogoUrl(Map<String, dynamic> branch) async {
|
|
||||||
try {
|
|
||||||
final token = branch['token']?.toString() ??
|
|
||||||
tokenStorage.getCurrentToken() ??
|
|
||||||
'';
|
|
||||||
if (token.isEmpty) return null;
|
|
||||||
|
|
||||||
final params = _clientDetailsParamsFromBranch(branch);
|
|
||||||
if (params == null) return null;
|
|
||||||
|
|
||||||
final url = Uri.parse('${Environment.apiUrl}getClientDetails').replace(
|
|
||||||
queryParameters: params,
|
|
||||||
);
|
|
||||||
|
|
||||||
final response = await http.get(
|
|
||||||
url,
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer $token',
|
|
||||||
'APP-SIGNATURE':
|
|
||||||
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode != 200) return null;
|
|
||||||
final data = jsonDecode(response.body);
|
|
||||||
if (data is! Map || data['status'] != 'success') return null;
|
|
||||||
|
|
||||||
final logo = data['data']?['client']?['client_logo']?.toString().trim();
|
|
||||||
if (logo == null || logo.isEmpty) return null;
|
|
||||||
return logo;
|
|
||||||
} catch (_) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> _getBranchSwitchRoute() async {
|
|
||||||
final postRaw = await tokenStorage.readValue('empAllowed_modules');
|
|
||||||
final postModules = postRaw != null && postRaw.isNotEmpty
|
|
||||||
? List<int>.from(jsonDecode(postRaw))
|
|
||||||
: <int>[];
|
|
||||||
|
|
||||||
if (postModules.isNotEmpty && postModules.contains(5)) {
|
|
||||||
return 'claimsOverviewDashboard';
|
|
||||||
}
|
|
||||||
return 'policies';
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onBranchSelected(Map<String, dynamic> branch) async {
|
|
||||||
final tokenStorage = TokenStorageService();
|
|
||||||
|
|
||||||
await tokenStorage.resetSessionAndSwitchBranch(branch);
|
|
||||||
|
|
||||||
if (!mounted) return;
|
|
||||||
|
|
||||||
_loadBranches();
|
|
||||||
|
|
||||||
final route = await _getBranchSwitchRoute();
|
|
||||||
|
|
||||||
if (!mounted) return;
|
|
||||||
Navigator.pushReplacementNamed(context, route);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final selectedLogo = selectedBranch == null
|
|
||||||
? null
|
|
||||||
: _logoByClientId[_clientKey(selectedBranch!)];
|
|
||||||
|
|
||||||
return AppBar(
|
|
||||||
automaticallyImplyLeading: false,
|
|
||||||
backgroundColor: const Color(0xFFBFEFEF),
|
|
||||||
elevation: 0,
|
|
||||||
title: Row(
|
|
||||||
children: [
|
|
||||||
Image.asset('assets/nhance_client_logo.png', height: 32),
|
|
||||||
const Spacer(),
|
|
||||||
if (selectedBranch != null)
|
|
||||||
_BranchPopup(
|
|
||||||
clientName: selectedBranch!['client_name']?.toString() ?? '',
|
|
||||||
branchName: selectedBranch!['branch_name']?.toString() ?? '',
|
|
||||||
selectedLogoUrl: selectedLogo,
|
|
||||||
branches: branches,
|
|
||||||
logoByClientId: _logoByClientId,
|
|
||||||
clientKeyBuilder: _clientKey,
|
|
||||||
onSelected: _onBranchSelected,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BranchPopup extends StatelessWidget {
|
|
||||||
final String clientName;
|
|
||||||
final String branchName;
|
|
||||||
final String? selectedLogoUrl;
|
|
||||||
final List<Map<String, dynamic>> branches;
|
|
||||||
final Map<String, String> logoByClientId;
|
|
||||||
final String Function(Map<String, dynamic>) clientKeyBuilder;
|
|
||||||
final Function(Map<String, dynamic>) onSelected;
|
|
||||||
|
|
||||||
const _BranchPopup({
|
|
||||||
required this.clientName,
|
|
||||||
required this.branchName,
|
|
||||||
required this.selectedLogoUrl,
|
|
||||||
required this.branches,
|
|
||||||
required this.logoByClientId,
|
|
||||||
required this.clientKeyBuilder,
|
|
||||||
required this.onSelected,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return PopupMenuButton<Map<String, dynamic>>(
|
|
||||||
tooltip: '',
|
|
||||||
offset: const Offset(0, 48),
|
|
||||||
onSelected: onSelected,
|
|
||||||
itemBuilder: (context) {
|
|
||||||
return branches.map((branch) {
|
|
||||||
final logoUrl = logoByClientId[clientKeyBuilder(branch)];
|
|
||||||
return PopupMenuItem<Map<String, dynamic>>(
|
|
||||||
value: branch,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
_BranchLogo(logoUrl: logoUrl, size: 24),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
branch['client_name']?.toString() ?? '',
|
|
||||||
style: const TextStyle(fontSize: 13),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
const Icon(Icons.location_on, size: 14, color: Colors.grey),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
branch['branch_name']?.toString() ?? '',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
_BranchLogo(logoUrl: selectedLogoUrl, size: 32),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Container(
|
|
||||||
height: 40,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(22),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.08),
|
|
||||||
blurRadius: 8,
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
clientName,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.location_on,
|
|
||||||
size: 14,
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
branchName,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
const Icon(
|
|
||||||
Icons.keyboard_arrow_down,
|
|
||||||
size: 20,
|
|
||||||
color: Colors.orange,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BranchLogo extends StatelessWidget {
|
|
||||||
final String? logoUrl;
|
|
||||||
final double size;
|
|
||||||
|
|
||||||
const _BranchLogo({
|
|
||||||
required this.logoUrl,
|
|
||||||
required this.size,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final url = logoUrl?.trim() ?? '';
|
|
||||||
if (url.isEmpty) {
|
|
||||||
return _placeholder();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
child: Image.network(
|
|
||||||
url,
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
errorBuilder: (_, __, ___) => _placeholder(),
|
|
||||||
loadingBuilder: (context, child, progress) {
|
|
||||||
if (progress == null) return child;
|
|
||||||
return SizedBox(
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
child: const Padding(
|
|
||||||
padding: EdgeInsets.all(4),
|
|
||||||
child: CircularProgressIndicator(strokeWidth: 1.5),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _placeholder() {
|
|
||||||
return Container(
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFE8F5F5),
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
border: Border.all(color: const Color(0xFFBFEFEF)),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
Icons.business,
|
|
||||||
size: size * 0.6,
|
|
||||||
color: const Color(0xFF00999E),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,797 +0,0 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'package:nhancepolicy/responsive.dart';
|
|
||||||
import 'package:nhancepolicy/service/api_service.dart';
|
|
||||||
import 'package:nhancepolicy/service/token_storage_service.dart';
|
|
||||||
import 'package:pinput/pinput.dart';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:flutter/gestures.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:jwt_decode/jwt_decode.dart';
|
|
||||||
|
|
||||||
import '../customAppBar/toastHelper.dart';
|
|
||||||
import '../models/platform_helper_mobile.dart'
|
|
||||||
if (dart.library.html) '../models/platform_helper_other.dart';
|
|
||||||
import 'branch/branch_selection_page.dart';
|
|
||||||
import 'config/environment.dart';
|
|
||||||
import 'package:nhancepolicy/logger.dart';
|
|
||||||
|
|
||||||
// import 'dart:html' as html;
|
|
||||||
|
|
||||||
class MyEmailVerify extends StatefulWidget {
|
|
||||||
final String type; // 'email' or 'mobile'
|
|
||||||
final String value;
|
|
||||||
|
|
||||||
const MyEmailVerify({super.key, required this.type, required this.value});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MyEmailVerify> createState() => _MyEmailVerifyState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MyEmailVerifyState extends State<MyEmailVerify> {
|
|
||||||
late ApiService apiService;
|
|
||||||
TextEditingController _otpController = TextEditingController();
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
|
||||||
late Timer _timer;
|
|
||||||
int _secondsRemaining = 60;
|
|
||||||
bool _isTimerRunning = false;
|
|
||||||
|
|
||||||
dynamic enrollmentEmpClientBranchId;
|
|
||||||
dynamic enrollmentEmpCodeString;
|
|
||||||
dynamic enrollmentEmpPrimaryId;
|
|
||||||
dynamic enrollmentGpaEmpName;
|
|
||||||
dynamic enrollmentClient_id;
|
|
||||||
dynamic enrollmentHrId;
|
|
||||||
List<dynamic> enrollmentAllowed_modules = [];
|
|
||||||
dynamic enrollmentEmp_status;
|
|
||||||
|
|
||||||
dynamic empCodeString;
|
|
||||||
dynamic empClientBranchId;
|
|
||||||
dynamic empPrimaryId;
|
|
||||||
dynamic gpaEmpName;
|
|
||||||
dynamic empClientId;
|
|
||||||
dynamic empHrId;
|
|
||||||
List<dynamic> empAllowed_modules = [];
|
|
||||||
dynamic _token;
|
|
||||||
dynamic _postToken;
|
|
||||||
dynamic clientName;
|
|
||||||
dynamic clientLogo;
|
|
||||||
dynamic emp_status;
|
|
||||||
dynamic fCMToken;
|
|
||||||
late String verificationId;
|
|
||||||
bool _isLoading = false;
|
|
||||||
|
|
||||||
late String _verificationId;
|
|
||||||
dynamic empMobileNo;
|
|
||||||
dynamic empEmailid;
|
|
||||||
final tokenService = TokenStorageService();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
apiService = ApiService(context);
|
|
||||||
startTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_timer.cancel();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void startTimer() {
|
|
||||||
_isTimerRunning = true;
|
|
||||||
_timer = Timer.periodic(Duration(seconds: 1), (Timer timer) {
|
|
||||||
setState(() {
|
|
||||||
if (_secondsRemaining > 0) {
|
|
||||||
_secondsRemaining--;
|
|
||||||
} else {
|
|
||||||
_isTimerRunning = false;
|
|
||||||
_timer.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void verifyOTP(String otp) async {
|
|
||||||
try {
|
|
||||||
if(otp.isEmpty){
|
|
||||||
ToastHelper.showErrorToast(context, 'Please enter OTP');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<String, dynamic> payload = (widget.type == 'mobile')
|
|
||||||
? {'otp': _otpController.text, 'mobile_no': widget.value}
|
|
||||||
: {'otp': _otpController.text, 'email': widget.value};
|
|
||||||
final response = await http.post(
|
|
||||||
Uri.parse(Environment.apiUrl + 'getVerifiedHrData'),
|
|
||||||
body: json.encode(payload),
|
|
||||||
headers: {
|
|
||||||
'APP-SIGNATURE':
|
|
||||||
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
|
|
||||||
HttpHeaders.contentTypeHeader: 'application/json',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
Map<String, dynamic> data = json.decode(response.body);
|
|
||||||
logDebug('Response data: $data');
|
|
||||||
|
|
||||||
final String mainStatus = data['status'] ?? 'failed';
|
|
||||||
final Map<String, dynamic> postEnrollment1 =
|
|
||||||
data['post_enrollment'] ?? {};
|
|
||||||
|
|
||||||
final String postStatus = postEnrollment1['status'] ?? 'failed';
|
|
||||||
|
|
||||||
/// ❌ BLOCK if BOTH failed
|
|
||||||
if (mainStatus != 'success' && postStatus != 'success') {
|
|
||||||
ToastHelper.showErrorToast(
|
|
||||||
context,
|
|
||||||
postEnrollment1['message'],
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ Safely extract preEnrollmentData
|
|
||||||
List<dynamic> preEnrollmentData = [];
|
|
||||||
if (data['data'] is List) {
|
|
||||||
preEnrollmentData = data['data'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ Safely extract postEnrollmentData
|
|
||||||
final postEnrollment = data['post_enrollment'] ?? {};
|
|
||||||
List<dynamic> postEnrollmentData = [];
|
|
||||||
if (postEnrollment['data'] is List) {
|
|
||||||
postEnrollmentData = postEnrollment['data'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract enrollment data
|
|
||||||
// List<dynamic> preEnrollmentData = data['data'] ?? [];
|
|
||||||
// Map<String, dynamic> postEnrollment = data['post_enrollment'] ?? {};
|
|
||||||
// List<dynamic> postEnrollmentData = postEnrollment['data'] ?? [];
|
|
||||||
|
|
||||||
// Save to global storage
|
|
||||||
final tokenStorage = TokenStorageService();
|
|
||||||
await tokenStorage.saveEnrollmentData(
|
|
||||||
preEnrollmentData,
|
|
||||||
postEnrollmentData,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Navigate to branch selection
|
|
||||||
// 🔥 Get all valid branches
|
|
||||||
final branches = tokenStorage.getCombinedBranches();
|
|
||||||
|
|
||||||
if (branches.length == 1) {
|
|
||||||
// ✅ ONLY ONE BRANCH → AUTO SELECT
|
|
||||||
final singleBranch = branches.first;
|
|
||||||
|
|
||||||
// Save selected branch
|
|
||||||
await tokenStorage.saveSelectedBranch(singleBranch);
|
|
||||||
|
|
||||||
final decodedToken = tokenStorage.getDecodedToken();
|
|
||||||
final token = tokenStorage.getCurrentToken();
|
|
||||||
|
|
||||||
if (decodedToken == null || token == null) {
|
|
||||||
ToastHelper.showErrorToast(context, 'Invalid token data');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save decoded session values
|
|
||||||
await tokenStorage.saveDecodedSessionData(decodedToken, token);
|
|
||||||
|
|
||||||
if (!mounted) return;
|
|
||||||
|
|
||||||
// 🚀 DIRECTLY GO TO POLICIES
|
|
||||||
Navigator.pushReplacementNamed(context, 'policies');
|
|
||||||
} else {
|
|
||||||
// 🧭 MULTIPLE BRANCHES → SHOW SELECTION PAGE
|
|
||||||
if (!mounted) return;
|
|
||||||
|
|
||||||
Navigator.pushReplacement(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const BranchSelectionPage(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// logDebug('data: $data');
|
|
||||||
// _token = data['data'];
|
|
||||||
// String status = data['status'];
|
|
||||||
//
|
|
||||||
// // Directly access the post_enrollment data
|
|
||||||
// Map<String, dynamic> post = data['post_enrollment'];
|
|
||||||
// logDebug('post: $post');
|
|
||||||
//
|
|
||||||
// _postToken = post['data'];
|
|
||||||
// String postStatus = post['status'];
|
|
||||||
//
|
|
||||||
// if (postStatus == 'success') {
|
|
||||||
// setState(() {
|
|
||||||
// _isLoading = false;
|
|
||||||
// });
|
|
||||||
// postSuccessData(post, data);
|
|
||||||
// } else if (status == 'success') {
|
|
||||||
// setState(() {
|
|
||||||
// _isLoading = false;
|
|
||||||
// });
|
|
||||||
// enrollmentSuccessData(data);
|
|
||||||
// } else {
|
|
||||||
// setState(() {
|
|
||||||
// _isLoading = false;
|
|
||||||
// });
|
|
||||||
// ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again');
|
|
||||||
// // Show a Snackbar if the OTP is invalid
|
|
||||||
// logDebug('Invalid OTP. Please try again');
|
|
||||||
// }
|
|
||||||
} else if (response.statusCode == 401) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
await tokenService.clearAll(); // 🔐 clears flutter_secure_storage
|
|
||||||
|
|
||||||
ToastHelper.showErrorToast(context, 'Session Out');
|
|
||||||
if (!context.mounted) return;
|
|
||||||
Navigator.pushNamedAndRemoveUntil(
|
|
||||||
context,
|
|
||||||
'hrLogin',
|
|
||||||
(route) => false,
|
|
||||||
);
|
|
||||||
} else if (response.statusCode == 429) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
Map<String, dynamic> data = json.decode(response.body);
|
|
||||||
if (data.containsKey('error')) {
|
|
||||||
final message = data['error']['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
} else {
|
|
||||||
final message = data['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
}
|
|
||||||
} else if (response.statusCode == 403) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
Map<String, dynamic> data = json.decode(response.body);
|
|
||||||
if (data.containsKey('error')) {
|
|
||||||
final message = data['error']['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
} else {
|
|
||||||
final message = data['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
}
|
|
||||||
} else if (response.statusCode == 451) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
Map<String, dynamic> data = json.decode(response.body);
|
|
||||||
if (data.containsKey('error')) {
|
|
||||||
final message = data['error']['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
} else {
|
|
||||||
final message = data['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
throw Exception('Failed to load data');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
logDebug('Error: $e');
|
|
||||||
ToastHelper.showWarningToast(context, 'Something went wrong');
|
|
||||||
// Show a Snackbar if there's an error while verifying OTP
|
|
||||||
logDebug('Failed to verify OTP. Please try again.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void postSuccessData(post, data) async {
|
|
||||||
String status = data['status'];
|
|
||||||
String postStatus = post['status'];
|
|
||||||
|
|
||||||
if (postStatus == 'success') {
|
|
||||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
prefs.setString('_postToken', post['data']);
|
|
||||||
|
|
||||||
// Decode the JWT token received from the API response
|
|
||||||
Map<String, dynamic>? decodedToken = Jwt.parseJwt(post['data']);
|
|
||||||
logDebug('postdecodedToken : $decodedToken');
|
|
||||||
empClientBranchId = decodedToken['ref_id'];
|
|
||||||
prefs.setString('empClientBranchId', empClientBranchId);
|
|
||||||
empCodeString = decodedToken['emp_code'].toString();
|
|
||||||
prefs.setString('empCode', empCodeString);
|
|
||||||
empPrimaryId = decodedToken['id'];
|
|
||||||
prefs.setString('empPrimaryId', empPrimaryId);
|
|
||||||
gpaEmpName = decodedToken['name'].toString();
|
|
||||||
prefs.setString('gpaEmpName', gpaEmpName);
|
|
||||||
empClientId = decodedToken['client_id'];
|
|
||||||
prefs.setString('empClientId', empClientId);
|
|
||||||
empHrId = decodedToken['id'];
|
|
||||||
prefs.setString('empHrId', empHrId);
|
|
||||||
empAllowed_modules = decodedToken['allowed_modules'];
|
|
||||||
prefs.setString('empAllowed_modules', jsonEncode(empAllowed_modules));
|
|
||||||
// emp_status = decodedToken['emp_status'];
|
|
||||||
// prefs.setString('emp_status', emp_status);
|
|
||||||
// getClientLogoAndDetails();
|
|
||||||
|
|
||||||
logDebug('Successfully Login');
|
|
||||||
|
|
||||||
// Redirect to another page
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == 'success') {
|
|
||||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
prefs.setString('enrollToken', data['data']);
|
|
||||||
|
|
||||||
// Decode the JWT token received from the API response
|
|
||||||
Map<String, dynamic>? decodedToken = Jwt.parseJwt(data['data']);
|
|
||||||
logDebug('postenrolldecodedToken : $decodedToken');
|
|
||||||
enrollmentEmpClientBranchId = decodedToken['ref_id'];
|
|
||||||
prefs.setString(
|
|
||||||
'enrollmentEmpClientBranchId', enrollmentEmpClientBranchId);
|
|
||||||
enrollmentEmpCodeString = decodedToken['emp_code'].toString();
|
|
||||||
prefs.setString('enrollmentEmpCodeString', enrollmentEmpCodeString);
|
|
||||||
enrollmentEmpPrimaryId = decodedToken['id'];
|
|
||||||
prefs.setString('enrollmentEmpPrimaryId', enrollmentEmpPrimaryId);
|
|
||||||
enrollmentGpaEmpName = decodedToken['name'].toString();
|
|
||||||
prefs.setString('enrollmentGpaEmpName', enrollmentGpaEmpName);
|
|
||||||
enrollmentClient_id = decodedToken['client_id'];
|
|
||||||
prefs.setString('enrollmentClient_id', enrollmentClient_id);
|
|
||||||
enrollmentHrId = decodedToken['id'];
|
|
||||||
prefs.setString('enrollmentHrId', enrollmentHrId);
|
|
||||||
enrollmentAllowed_modules = decodedToken['allowed_modules'];
|
|
||||||
prefs.setString(
|
|
||||||
'enrollmentAllowed_modules', jsonEncode(enrollmentAllowed_modules));
|
|
||||||
// enrollmentEmp_status = decodedToken['emp_status'];
|
|
||||||
// prefs.setString('enrollmentEmp_status', enrollmentEmp_status);
|
|
||||||
}
|
|
||||||
|
|
||||||
getClientLogoAndDetails(empClientId, empClientBranchId, enrollmentClient_id,
|
|
||||||
enrollmentEmpClientBranchId, post['data']);
|
|
||||||
|
|
||||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
final _postToken = prefs.getString('_postToken');
|
|
||||||
|
|
||||||
if (_postToken != null && _postToken.isNotEmpty) {
|
|
||||||
ToastHelper.showSuccessToast(context, 'Successfully Logged In');
|
|
||||||
// if (emp_status == 'enrolled' || emp_status == 'active') {
|
|
||||||
Navigator.pushReplacementNamed(context, 'policies');
|
|
||||||
// } else {
|
|
||||||
// Navigator.pushReplacementNamed(context, 'empDetails');
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void enrollmentSuccessData(data) async {
|
|
||||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
prefs.setString('enrollToken', data['data']);
|
|
||||||
|
|
||||||
// Decode the JWT token received from the API response
|
|
||||||
Map<String, dynamic>? decodedToken = Jwt.parseJwt(data['data']);
|
|
||||||
logDebug('enrolldecodedToken : $decodedToken');
|
|
||||||
enrollmentEmpClientBranchId = decodedToken['ref_id'];
|
|
||||||
prefs.setString('enrollmentEmpClientBranchId', enrollmentEmpClientBranchId);
|
|
||||||
enrollmentEmpCodeString = decodedToken['emp_code'].toString();
|
|
||||||
prefs.setString('enrollmentEmpCodeString', enrollmentEmpCodeString);
|
|
||||||
enrollmentEmpPrimaryId = decodedToken['id'];
|
|
||||||
prefs.setString('enrollmentEmpPrimaryId', enrollmentEmpPrimaryId);
|
|
||||||
enrollmentGpaEmpName = decodedToken['name'].toString();
|
|
||||||
prefs.setString('enrollmentGpaEmpName', enrollmentGpaEmpName);
|
|
||||||
enrollmentClient_id = decodedToken['client_id'];
|
|
||||||
prefs.setString('enrollmentClient_id', enrollmentClient_id);
|
|
||||||
enrollmentHrId = decodedToken['id'];
|
|
||||||
logDebug("enrollmentHrId- $enrollmentHrId");
|
|
||||||
prefs.setString('enrollmentHrId', enrollmentHrId);
|
|
||||||
enrollmentAllowed_modules = decodedToken['allowed_modules'];
|
|
||||||
prefs.setString(
|
|
||||||
'enrollmentAllowed_modules', jsonEncode(enrollmentAllowed_modules));
|
|
||||||
// enrollmentEmp_status = decodedToken['emp_status'];
|
|
||||||
// prefs.setString('enrollmentEmp_status', enrollmentEmp_status);
|
|
||||||
getClientLogoAndDetails(empClientId, empClientBranchId, enrollmentClient_id,
|
|
||||||
enrollmentEmpClientBranchId, data['data']);
|
|
||||||
logDebug('Successfully Login');
|
|
||||||
|
|
||||||
// Redirect to another page
|
|
||||||
final enrollToken = prefs.getString('enrollToken');
|
|
||||||
|
|
||||||
if (enrollToken != null && enrollToken.isNotEmpty) {
|
|
||||||
ToastHelper.showSuccessToast(context, 'Successfully Logged In');
|
|
||||||
// if (emp_status == 'enrolled' || emp_status == 'active') {
|
|
||||||
// Navigator.pushReplacementNamed(context, 'home');
|
|
||||||
// } else {
|
|
||||||
Navigator.pushReplacementNamed(context, 'policies');
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _resendOTP() {
|
|
||||||
setState(() {
|
|
||||||
_secondsRemaining = 60;
|
|
||||||
_isTimerRunning = true;
|
|
||||||
});
|
|
||||||
startTimer();
|
|
||||||
verifyMobileAndEmailNumber();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> verifyMobileAndEmailNumber() async {
|
|
||||||
try {
|
|
||||||
// var enteredMobileNumber = mobileController.text;
|
|
||||||
final Map<String, dynamic> payload = (widget.type == 'mobile')
|
|
||||||
? {'mobile_number': widget.value}
|
|
||||||
: {'email': widget.value};
|
|
||||||
|
|
||||||
String apiEndpoint = (widget.type == 'mobile')
|
|
||||||
? Environment.apiUrl + 'verifyHrWithMobileNumber'
|
|
||||||
: Environment.apiUrl + 'verifyHrWithEmail';
|
|
||||||
|
|
||||||
final response = await http.post(
|
|
||||||
Uri.parse(apiEndpoint),
|
|
||||||
body: json.encode(payload),
|
|
||||||
headers: {
|
|
||||||
'APP-SIGNATURE':
|
|
||||||
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
|
|
||||||
HttpHeaders.contentTypeHeader: 'application/json',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
Map<String, dynamic> data = json.decode(response.body);
|
|
||||||
bool userVerification = data['data']['user_verification'];
|
|
||||||
String message = data['data']['message'];
|
|
||||||
if (userVerification) {
|
|
||||||
ToastHelper.showSuccessToast(
|
|
||||||
context, 'Verification code sent to ${widget.value}');
|
|
||||||
} else {
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
logDebug('Invalid mobile number');
|
|
||||||
}
|
|
||||||
} else if (response.statusCode == 403) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
Map<String, dynamic> data = json.decode(response.body);
|
|
||||||
final message = data['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
} else if (response.statusCode == 451) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
Map<String, dynamic> data = json.decode(response.body);
|
|
||||||
final message = data['error']['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
} else {
|
|
||||||
ToastHelper.showErrorToast(context, 'Something went wrong');
|
|
||||||
throw Exception('Failed to verify mobile number');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
ToastHelper.showErrorToast(context, 'Something went wrong');
|
|
||||||
logDebug('Error: $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> getClientLogoAndDetails(empClientId, empClientBranchId,
|
|
||||||
enrollmentClientId, enrollmentClientBranchId, token) async {
|
|
||||||
var url = Uri.parse(Environment.apiUrl +
|
|
||||||
'getClientDetails?post_client_id=$empClientId&post_branch_id=$empClientBranchId&pre_client_id=$enrollmentClientId&pre_branch_id=$enrollmentClientBranchId');
|
|
||||||
try {
|
|
||||||
var response = await http.get(
|
|
||||||
url,
|
|
||||||
headers: {
|
|
||||||
'APP-SIGNATURE':
|
|
||||||
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
|
|
||||||
'Authorization':
|
|
||||||
'Bearer $token', // Add token to the Authorization header
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
// logDebug('response.statusCode == 200');
|
|
||||||
Map<String, dynamic> data = json.decode(response.body);
|
|
||||||
// logDebug(data);
|
|
||||||
|
|
||||||
if (data.containsKey('data')) {
|
|
||||||
dynamic clientDetails = data['data'];
|
|
||||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
prefs.setString('clientLogo', clientDetails['client']['client_logo']);
|
|
||||||
prefs.setString('clientName', clientDetails['client']['client_name']);
|
|
||||||
setState(() {
|
|
||||||
// dynamic clientDetails = data['data'];
|
|
||||||
// logDebug(clientDetails);
|
|
||||||
clientName = clientDetails['client']['client_name'];
|
|
||||||
logDebug(clientName);
|
|
||||||
clientLogo = clientDetails['client']['client_logo'];
|
|
||||||
logDebug(clientLogo);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Handle other status messages if needed
|
|
||||||
// ToastHelper.showErrorToast(
|
|
||||||
// context, 'API request failed with status: ${data['status']}');
|
|
||||||
logDebug('API request failed with status: ${data['status']}');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Handle other status codes
|
|
||||||
// ToastHelper.showErrorToast(
|
|
||||||
// context, 'Request failed with status: ${response.statusCode}');
|
|
||||||
logDebug('Request failed with status: ${response.statusCode}');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Handle exceptions
|
|
||||||
logDebug('Exception occurred: $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final Size _size = MediaQuery.of(context).size;
|
|
||||||
final defaultPinTheme = PinTheme(
|
|
||||||
width: 56,
|
|
||||||
height: 56,
|
|
||||||
textStyle: TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
color: Color.fromRGBO(30, 60, 87, 1),
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border.all(color: Color.fromRGBO(234, 239, 243, 1)),
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final focusedPinTheme = defaultPinTheme.copyDecorationWith(
|
|
||||||
border: Border.all(color: Color.fromRGBO(114, 178, 238, 1)),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
);
|
|
||||||
|
|
||||||
final submittedPinTheme = defaultPinTheme.copyWith(
|
|
||||||
decoration: defaultPinTheme.decoration?.copyWith(
|
|
||||||
color: Color.fromRGBO(234, 239, 243, 1),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
height: _size.height,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.topRight,
|
|
||||||
end: Alignment.bottomLeft,
|
|
||||||
colors: [
|
|
||||||
Color(0xFF00B6AC),
|
|
||||||
Color(0xFF83E0DE),
|
|
||||||
Color(0xFF01B4A8),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity, // ✅ fixed web width
|
|
||||||
height: _size.height, // ✅ fixed web height (IMPORTANT)
|
|
||||||
margin: const EdgeInsets.all(60),
|
|
||||||
clipBehavior: Clip.hardEdge,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(40)),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
// ================= LEFT IMAGE =================
|
|
||||||
Expanded(
|
|
||||||
flex: 5,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: const BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(40),
|
|
||||||
bottomLeft: Radius.circular(40),
|
|
||||||
),
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/hrLogin.png',
|
|
||||||
width: double.infinity,
|
|
||||||
height: double.infinity,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// ================= RIGHT FORM =================
|
|
||||||
Expanded(
|
|
||||||
flex: 7,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
|
||||||
child: Form(
|
|
||||||
key: _formKey,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Image.asset(
|
|
||||||
'assets/Nhance-Logo-Final 1.png',
|
|
||||||
width: 300,
|
|
||||||
height: 100,
|
|
||||||
),
|
|
||||||
SizedBox(height: 15),
|
|
||||||
Container(
|
|
||||||
margin: Responsive.isDesktop(context)
|
|
||||||
? EdgeInsets.symmetric(horizontal: 150)
|
|
||||||
: EdgeInsets.symmetric(horizontal: 0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Welcome to Nhance",
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 15),
|
|
||||||
Container(
|
|
||||||
margin: Responsive.isDesktop(context)
|
|
||||||
? EdgeInsets.symmetric(horizontal: 150)
|
|
||||||
: EdgeInsets.symmetric(horizontal: 0),
|
|
||||||
child: RichText(
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
text: TextSpan(
|
|
||||||
text:
|
|
||||||
"Please enter the one-time usage code sent to your ",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
height: 1.5,
|
|
||||||
color: Color(0xFF000000)),
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: widget.type == 'mobile'
|
|
||||||
? 'mobile number ${widget.value}'
|
|
||||||
: 'Email Id ${widget.value}',
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: widget.type == 'mobile'
|
|
||||||
? ' (Change Mobile)'
|
|
||||||
: ' (Change Email)',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Color(
|
|
||||||
0xFFE26728)), // Change color as desired
|
|
||||||
recognizer: TapGestureRecognizer()
|
|
||||||
..onTap = () {
|
|
||||||
// Navigate to the page where the user can change the phone number
|
|
||||||
Navigator.pushNamed(
|
|
||||||
context, 'hrLogin');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 15),
|
|
||||||
Container(
|
|
||||||
margin: Responsive.isDesktop(context)
|
|
||||||
? EdgeInsets.symmetric(horizontal: 150)
|
|
||||||
: EdgeInsets.symmetric(horizontal: 0),
|
|
||||||
child: Pinput(
|
|
||||||
length: 6,
|
|
||||||
// defaultPinTheme: defaultPinTheme,
|
|
||||||
// focusedPinTheme: focusedPinTheme,
|
|
||||||
// submittedPinTheme: submittedPinTheme,
|
|
||||||
showCursor: true,
|
|
||||||
controller: _otpController,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 15),
|
|
||||||
Container(
|
|
||||||
margin: Responsive.isDesktop(context)
|
|
||||||
? EdgeInsets.symmetric(horizontal: 150)
|
|
||||||
: EdgeInsets.symmetric(horizontal: 0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment
|
|
||||||
.end, // Align text to the right
|
|
||||||
children: [
|
|
||||||
_isTimerRunning
|
|
||||||
? Text(
|
|
||||||
"Resend OTP in $_secondsRemaining seconds",
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
color: Colors.black),
|
|
||||||
)
|
|
||||||
: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
_resendOTP();
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
"Resend OTP",
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
color: Colors.blue),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 15),
|
|
||||||
Container(
|
|
||||||
margin: Responsive.isDesktop(context)
|
|
||||||
? EdgeInsets.symmetric(horizontal: 150)
|
|
||||||
: EdgeInsets.symmetric(horizontal: 0),
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 45,
|
|
||||||
child: ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Color(0xFF00989E),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
if (_formKey.currentState!.validate()) {
|
|
||||||
_formKey.currentState!
|
|
||||||
.save(); // Save form fields before calling verifyOTP
|
|
||||||
verifyOTP(_otpController.text);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
"Submit",
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
color: Color(0xFFFFFFFF)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 15),
|
|
||||||
Container(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 8),
|
|
||||||
child: RichText(
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
text: TextSpan(
|
|
||||||
text: 'By continuing, you agree with our ',
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
color: Colors.black,
|
|
||||||
fontSize: 9,
|
|
||||||
),
|
|
||||||
children: <TextSpan>[
|
|
||||||
TextSpan(
|
|
||||||
text: 'privacy policy ',
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
color: Color(0xFF00989E),
|
|
||||||
fontSize: 9,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: 'and ',
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
color: Colors.black,
|
|
||||||
fontSize: 9,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: 'terms of use',
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
color: Color(0xFF00989E),
|
|
||||||
fontSize: 9,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2453
lib/empDetails.dart
2952
lib/empReview.dart
2145
lib/home.dart
1528
lib/hrHome.dart
663
lib/hrLogin.dart
@ -1,663 +0,0 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/gestures.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:nhancepolicy/customAppBar/toastHelper.dart';
|
|
||||||
import 'package:nhancepolicy/hrVerify.dart';
|
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'package:nhancepolicy/responsive.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'package:flutter_animated_button/flutter_animated_button.dart';
|
|
||||||
import 'package:nhancepolicy/service/token_storage_service.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
|
|
||||||
import 'config/environment.dart';
|
|
||||||
import 'email_verify.dart';
|
|
||||||
import 'package:nhancepolicy/logger.dart';
|
|
||||||
|
|
||||||
class MyHrLogin extends StatefulWidget {
|
|
||||||
const MyHrLogin({Key? key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MyHrLogin> createState() => _MyPhoneState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MyPhoneState extends State<MyHrLogin> {
|
|
||||||
final TextEditingController emailMobileController = TextEditingController();
|
|
||||||
// final TextEditingController emailController = TextEditingController();
|
|
||||||
// TextEditingController countryController = TextEditingController();
|
|
||||||
// TextEditingController mobileController = TextEditingController();
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
|
||||||
bool _isCheckingToken = false;
|
|
||||||
dynamic empMobileNo;
|
|
||||||
dynamic empEmailid;
|
|
||||||
late String _verificationId;
|
|
||||||
int? _resendToken;
|
|
||||||
bool _isLoading = false;
|
|
||||||
bool isEmailFieldVisible = false;
|
|
||||||
final tokenService = TokenStorageService();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
logDebug('vndbbcbdskbvkjs1');
|
|
||||||
// countryController.text = "+91";
|
|
||||||
super.initState();
|
|
||||||
clearLocalStorageWhenStarts('initState');
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLocalStorageWhenStarts(fromData) async {
|
|
||||||
logDebug(fromData);
|
|
||||||
logDebug('Clearing secure storage on app start');
|
|
||||||
|
|
||||||
await tokenService.clearAll();
|
|
||||||
|
|
||||||
logDebug('Secure Storage Cleared');
|
|
||||||
}
|
|
||||||
|
|
||||||
void toggleField() {
|
|
||||||
setState(() {
|
|
||||||
isEmailFieldVisible = !isEmailFieldVisible;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> verifyMobileAndEmailNumber() async {
|
|
||||||
await tokenService.clearAll();
|
|
||||||
logDebug('verifyMobileAndEmailNumber :- Local Storage Clear');
|
|
||||||
try {
|
|
||||||
if (_formKey.currentState!.validate()) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
String input = emailMobileController.text.trim();
|
|
||||||
|
|
||||||
bool isEmail = RegExp(r'^[^@]+@[^@]+\.[^@]+$').hasMatch(input);
|
|
||||||
bool isMobile = RegExp(r'^[0-9]{10}$').hasMatch(input);
|
|
||||||
|
|
||||||
if (isEmail) {
|
|
||||||
setState(() {
|
|
||||||
isEmailFieldVisible = true;
|
|
||||||
});
|
|
||||||
logDebug("User entered Email: $input");
|
|
||||||
} else if (isMobile) {
|
|
||||||
isEmailFieldVisible = false;
|
|
||||||
logDebug("User entered Mobile: $input");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the API and the payload based on the visible field
|
|
||||||
String apiEndpoint = isEmailFieldVisible
|
|
||||||
? Environment.apiUrl + 'verifyHrWithEmail'
|
|
||||||
: Environment.apiUrl + 'verifyHrWithMobileNumber';
|
|
||||||
|
|
||||||
Map<String, dynamic> payload = isEmailFieldVisible
|
|
||||||
? {'email': emailMobileController.text}
|
|
||||||
: {'mobile_number': emailMobileController.text};
|
|
||||||
|
|
||||||
// var enteredMobileNumber = mobileController.text;
|
|
||||||
final response = await http.post(
|
|
||||||
Uri.parse(apiEndpoint),
|
|
||||||
body: json.encode(payload),
|
|
||||||
headers: {
|
|
||||||
'APP-SIGNATURE':
|
|
||||||
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
|
|
||||||
HttpHeaders.contentTypeHeader: 'application/json',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
Map<String, dynamic> data = json.decode(response.body);
|
|
||||||
bool userVerification = data['data']['user_verification'];
|
|
||||||
String message = data['data']['message'];
|
|
||||||
if (userVerification) {
|
|
||||||
if (isEmailFieldVisible) {
|
|
||||||
logDebug('isEmailFieldVisible $isEmailFieldVisible');
|
|
||||||
await tokenService.writeValue(
|
|
||||||
'empEmail',
|
|
||||||
emailMobileController.text,
|
|
||||||
);
|
|
||||||
|
|
||||||
// logDebug('${emailMobileController.text}');
|
|
||||||
// final message = 'Verification code sent to ${emailMobileController.text}';
|
|
||||||
ToastHelper.showSuccessToast(context,
|
|
||||||
'Verification code sent to ${emailMobileController.text}');
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => MyEmailVerify(
|
|
||||||
type: 'email',
|
|
||||||
value: emailMobileController.text
|
|
||||||
.trim(), // Pass the phone number
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// final message = 'Verification code sent to ${emailMobileController.text}';
|
|
||||||
ToastHelper.showSuccessToast(context,
|
|
||||||
'Verification code sent to ${emailMobileController.text}');
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => MyEmailVerify(
|
|
||||||
type: 'mobile',
|
|
||||||
value: emailMobileController.text
|
|
||||||
.trim(), // Pass the phone number
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToastHelper.showSuccessToast(context, message);
|
|
||||||
} else {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
logDebug('Invalid mobile number');
|
|
||||||
}
|
|
||||||
} else if (response.statusCode == 429) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
Map<String, dynamic> data = json.decode(response.body);
|
|
||||||
if (data.containsKey('error')) {
|
|
||||||
final message = data['error']['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
} else {
|
|
||||||
final message = data['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
}
|
|
||||||
} else if (response.statusCode == 403) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
Map<String, dynamic> data = json.decode(response.body);
|
|
||||||
if (data.containsKey('error')) {
|
|
||||||
final message = data['error']['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
} else {
|
|
||||||
final message = data['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
}
|
|
||||||
} else if (response.statusCode == 451) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
Map<String, dynamic> data = json.decode(response.body);
|
|
||||||
if (data.containsKey('error')) {
|
|
||||||
final message = data['error']['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
} else {
|
|
||||||
final message = data['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
Map<String, dynamic> data = json.decode(response.body);
|
|
||||||
final message = data['message'];
|
|
||||||
ToastHelper.showErrorToast(context, message);
|
|
||||||
throw Exception('Failed to verify mobile number');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
ToastHelper.showErrorToast(context, 'Something went wrong');
|
|
||||||
logDebug('Error: $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Future<void> _verifyPhoneNumber() async {
|
|
||||||
// var enteredMobileNumber = mobileController.text;
|
|
||||||
// var countryCode = countryController.text;
|
|
||||||
// logDebug('${countryCode + enteredMobileNumber}');
|
|
||||||
// await _auth.verifyPhoneNumber(
|
|
||||||
// phoneNumber: '${countryCode + enteredMobileNumber}',
|
|
||||||
// timeout: const Duration(seconds: 60),
|
|
||||||
// verificationCompleted: (PhoneAuthCredential credential) async {
|
|
||||||
// await _auth.signInWithCredential(credential);
|
|
||||||
// // ToastHelper.showSuccessToast(context, 'Verified Successfully!');
|
|
||||||
// // setState(() {
|
|
||||||
// // _isLoading = false;
|
|
||||||
// // });
|
|
||||||
// },
|
|
||||||
// verificationFailed: (FirebaseAuthException e) {
|
|
||||||
// logDebug('Verification Failed: ${e.code} - ${e.message}');
|
|
||||||
// String errorMessage;
|
|
||||||
// if (e.code == 'invalid-app-credential') {
|
|
||||||
// errorMessage = 'Invalid Credential. Please try again.';
|
|
||||||
// } else if (e.code == 'invalid-phone-number') {
|
|
||||||
// errorMessage = 'The provided phone number is not valid.';
|
|
||||||
// } else if (e.code == 'too-many-requests') {
|
|
||||||
// errorMessage = 'Too many requests. Try again later.';
|
|
||||||
// } else {
|
|
||||||
// errorMessage = 'Verification Failed: ${e.message}';
|
|
||||||
// }
|
|
||||||
// ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
// SnackBar(
|
|
||||||
// content: Text(errorMessage),
|
|
||||||
// duration: Duration(seconds: 2),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// ToastHelper.showSuccessToast(context, errorMessage);
|
|
||||||
// setState(() {
|
|
||||||
// _isLoading = false;
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// codeSent: (String verificationId, int? resendToken) async {
|
|
||||||
// setState(() {
|
|
||||||
// _verificationId = verificationId;
|
|
||||||
// _resendToken = resendToken;
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// // When getting the verificationId
|
|
||||||
// SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
// prefs.setString('verificationId', _verificationId);
|
|
||||||
//
|
|
||||||
// ToastHelper.showSuccessToast(
|
|
||||||
// context, 'Verification code sent to ${enteredMobileNumber}');
|
|
||||||
//
|
|
||||||
// Navigator.push(
|
|
||||||
// context,
|
|
||||||
// MaterialPageRoute(
|
|
||||||
// builder: (context) => MyHrVerify(
|
|
||||||
// verificationId: _verificationId,
|
|
||||||
// mobileNumber: enteredMobileNumber,
|
|
||||||
// resendToken: _resendToken,
|
|
||||||
// onResendCode: _resendCode, // Pass the phone number
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// setState(() {
|
|
||||||
// _isLoading = false;
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// codeAutoRetrievalTimeout: (String verificationId) async {
|
|
||||||
// setState(() {
|
|
||||||
// _verificationId = verificationId;
|
|
||||||
// });
|
|
||||||
// final prefs = await SharedPreferences.getInstance();
|
|
||||||
// await prefs.setString('verificationId', verificationId);
|
|
||||||
// ToastHelper.showSuccessToast(context, 'Code auto-retrieval timed out.');
|
|
||||||
// setState(() {
|
|
||||||
// _isLoading = false;
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// void _resendCode(String mobileNumber, int? resendToken) async {
|
|
||||||
// await _auth.verifyPhoneNumber(
|
|
||||||
// phoneNumber: '${countryController.text + mobileNumber}',
|
|
||||||
// timeout: const Duration(seconds: 60),
|
|
||||||
// forceResendingToken: resendToken,
|
|
||||||
// verificationCompleted: (PhoneAuthCredential credential) async {
|
|
||||||
// await _auth.signInWithCredential(credential);
|
|
||||||
// },
|
|
||||||
// verificationFailed: (FirebaseAuthException e) {
|
|
||||||
// if (e.code == 'invalid-phone-number') {
|
|
||||||
// logDebug('The provided phone number is not valid.');
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// codeSent: (String verificationId, int? resendToken) async {
|
|
||||||
// final prefs = await SharedPreferences.getInstance();
|
|
||||||
// await prefs.setString('verificationId', verificationId);
|
|
||||||
// setState(() {
|
|
||||||
// _verificationId = verificationId;
|
|
||||||
// _resendToken = resendToken;
|
|
||||||
// });
|
|
||||||
// ToastHelper.showSuccessToast(
|
|
||||||
// context, 'Verification code resent to ${mobileNumber}');
|
|
||||||
// Navigator.push(
|
|
||||||
// context,
|
|
||||||
// MaterialPageRoute(
|
|
||||||
// builder: (context) => MyHrVerify(
|
|
||||||
// verificationId: _verificationId,
|
|
||||||
// mobileNumber: mobileNumber,
|
|
||||||
// resendToken: _resendToken,
|
|
||||||
// onResendCode: _resendCode,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// codeAutoRetrievalTimeout: (String verificationId) async {
|
|
||||||
// final prefs = await SharedPreferences.getInstance();
|
|
||||||
// await prefs.setString('verificationId', verificationId);
|
|
||||||
// setState(() {
|
|
||||||
// _verificationId = verificationId;
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final Size _size = MediaQuery.of(context).size;
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
height: _size.height,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.topRight,
|
|
||||||
end: Alignment.bottomLeft,
|
|
||||||
colors: [
|
|
||||||
Color(0xFF00B6AC),
|
|
||||||
Color(0xFF83E0DE),
|
|
||||||
Color(0xFF01B4A8),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity, // ✅ fixed web width
|
|
||||||
height: _size.height, // ✅ fixed web height (IMPORTANT)
|
|
||||||
margin: const EdgeInsets.all(60),
|
|
||||||
clipBehavior: Clip.hardEdge,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(40)),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
// ================= LEFT IMAGE =================
|
|
||||||
Expanded(
|
|
||||||
flex: 5,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: const BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(40),
|
|
||||||
bottomLeft: Radius.circular(40),
|
|
||||||
),
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/hrLogin.png',
|
|
||||||
width: double.infinity,
|
|
||||||
height: double.infinity,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// ================= RIGHT FORM =================
|
|
||||||
Expanded(
|
|
||||||
flex: 7,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
|
||||||
child: Form(
|
|
||||||
key: _formKey,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Image.asset(
|
|
||||||
'assets/Nhance-Logo-Final 1.png',
|
|
||||||
width: 300,
|
|
||||||
height: 100,
|
|
||||||
),
|
|
||||||
SizedBox(height: 25),
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.symmetric(horizontal: 150),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
"Login with your ${isEmailFieldVisible ? 'email' : 'mobile number'} and OTP to review and enroll for exciting health benefits for you and your family",
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF000000)),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 20),
|
|
||||||
Container(
|
|
||||||
height: 55,
|
|
||||||
margin:
|
|
||||||
const EdgeInsets.symmetric(horizontal: 150),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border:
|
|
||||||
Border.all(width: 1, color: Colors.grey),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: TextFormField(
|
|
||||||
controller: emailMobileController,
|
|
||||||
keyboardType: TextInputType.text,
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
border: InputBorder.none,
|
|
||||||
hintText: "Email / Mobile Number ",
|
|
||||||
contentPadding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
),
|
|
||||||
validator: (value) {
|
|
||||||
if (value == null ||
|
|
||||||
value.trim().isEmpty) {
|
|
||||||
return "Please enter email or mobile number";
|
|
||||||
}
|
|
||||||
|
|
||||||
String input = value.trim();
|
|
||||||
|
|
||||||
// ❌ Reject all spaces
|
|
||||||
if (input.contains(' ')) {
|
|
||||||
return "No spaces allowed";
|
|
||||||
}
|
|
||||||
|
|
||||||
final emailRegex =
|
|
||||||
RegExp(r'^[^@]+@[^@]+\.[^@]+$');
|
|
||||||
final mobileRegex =
|
|
||||||
RegExp(r'^[0-9]{10}$');
|
|
||||||
|
|
||||||
bool isEmailFormat =
|
|
||||||
emailRegex.hasMatch(input);
|
|
||||||
bool isMobileFormat =
|
|
||||||
mobileRegex.hasMatch(input);
|
|
||||||
|
|
||||||
// ---------------------------
|
|
||||||
// 🛑 MOBILE VALIDATION
|
|
||||||
// ---------------------------
|
|
||||||
if (RegExp(r'^[0-9]+$').hasMatch(input)) {
|
|
||||||
if (input.length != 10) {
|
|
||||||
return "Mobile number must be exactly 10 digits";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------
|
|
||||||
// 🛑 EMAIL VALIDATION
|
|
||||||
// ---------------------------
|
|
||||||
|
|
||||||
// Reject anything that has '@' but is NOT a valid email format
|
|
||||||
if (input.contains('@') &&
|
|
||||||
!isEmailFormat) {
|
|
||||||
return "Enter a valid email address";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reject email with extra digits at the end
|
|
||||||
if (input.contains('@') &&
|
|
||||||
RegExp(r'\d+$').hasMatch(input)) {
|
|
||||||
return "Email cannot contain extra numbers";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reject email+mobile combination
|
|
||||||
if (input.contains('@') &&
|
|
||||||
RegExp(r'\d{10}$').hasMatch(input)) {
|
|
||||||
return "Enter only email OR mobile number";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------
|
|
||||||
// 🛑 MIXED CONTENT (letters + digits but NOT email)
|
|
||||||
// ---------------------------
|
|
||||||
bool hasLetters =
|
|
||||||
RegExp(r'[A-Za-z]').hasMatch(input);
|
|
||||||
bool hasDigits =
|
|
||||||
RegExp(r'[0-9]').hasMatch(input);
|
|
||||||
|
|
||||||
if ((hasLetters && hasDigits) &&
|
|
||||||
!input.contains('@')) {
|
|
||||||
return "Enter only email OR 10-digit mobile number";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------
|
|
||||||
// 🟢 FINAL CHECK
|
|
||||||
// ---------------------------
|
|
||||||
if (!isEmailFormat && !isMobileFormat) {
|
|
||||||
return "Enter a valid email or 10-digit mobile number";
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
SizedBox(height: 20),
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.symmetric(horizontal: 150),
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 45,
|
|
||||||
child: ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Color(0xFF00989E),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: _isLoading
|
|
||||||
? null
|
|
||||||
: verifyMobileAndEmailNumber,
|
|
||||||
child: _isLoading
|
|
||||||
? const CircularProgressIndicator(
|
|
||||||
valueColor:
|
|
||||||
AlwaysStoppedAnimation<Color>(
|
|
||||||
Color(0xFF00989E),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Text(
|
|
||||||
"Login with Email / Mobile OTP",
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
color: const Color(0xFFFFFFFF),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 20),
|
|
||||||
Container(
|
|
||||||
width: double.infinity,
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(vertical: 8),
|
|
||||||
child: RichText(
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
text: TextSpan(
|
|
||||||
text: 'By continuing, you agree with our ',
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
color: Colors.black,
|
|
||||||
fontSize: 9,
|
|
||||||
),
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: 'privacy policy ',
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
color: const Color(0xFFE26828),
|
|
||||||
fontSize: 9,
|
|
||||||
// decoration: TextDecoration.underline,
|
|
||||||
),
|
|
||||||
recognizer: TapGestureRecognizer()
|
|
||||||
..onTap = () async {
|
|
||||||
final url = Uri.parse(
|
|
||||||
'https://nhanceindia.in/privacy-policy/');
|
|
||||||
if (await canLaunchUrl(url)) {
|
|
||||||
await launchUrl(url,
|
|
||||||
mode: LaunchMode
|
|
||||||
.externalApplication);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: 'and ',
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
color: Colors.black,
|
|
||||||
fontSize: 9,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: 'terms of use',
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
color: const Color(0xFFE26828),
|
|
||||||
fontSize: 9,
|
|
||||||
// decoration: TextDecoration.underline,
|
|
||||||
),
|
|
||||||
recognizer: TapGestureRecognizer()
|
|
||||||
..onTap = () async {
|
|
||||||
final url = Uri.parse(
|
|
||||||
'https://nhanceindia.in/privacy-policy/');
|
|
||||||
if (await canLaunchUrl(url)) {
|
|
||||||
await launchUrl(url,
|
|
||||||
mode: LaunchMode
|
|
||||||
.externalApplication);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Container(
|
|
||||||
// width: double
|
|
||||||
// .infinity, // Make the footer full width
|
|
||||||
// child: Container(
|
|
||||||
// alignment: Alignment.bottomCenter,
|
|
||||||
// padding:
|
|
||||||
// EdgeInsets.symmetric(vertical: 8),
|
|
||||||
// child: RichText(
|
|
||||||
// textAlign: TextAlign.center,
|
|
||||||
// text: TextSpan(
|
|
||||||
// text:
|
|
||||||
// 'By continuing, you agree with our ',
|
|
||||||
// style: GoogleFonts.poppins(
|
|
||||||
// color: Colors.black,
|
|
||||||
// fontSize: 9,
|
|
||||||
// ),
|
|
||||||
// children: <TextSpan>[
|
|
||||||
// TextSpan(
|
|
||||||
// text: 'privacy policy ',
|
|
||||||
// style: GoogleFonts.poppins(
|
|
||||||
// color: Color(0xFFE26828),
|
|
||||||
// fontSize: 9,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// TextSpan(
|
|
||||||
// text: 'and ',
|
|
||||||
// style: GoogleFonts.poppins(
|
|
||||||
// color: Colors.black,
|
|
||||||
// fontSize: 9,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// TextSpan(
|
|
||||||
// text: 'terms of use',
|
|
||||||
// style: GoogleFonts.poppins(
|
|
||||||
// color: Color(0xFFE26828),
|
|
||||||
// fontSize: 9,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,930 +0,0 @@
|
|||||||
// import 'package:firebase_auth/firebase_auth.dart';
|
|
||||||
// import 'package:flutter/material.dart';
|
|
||||||
// import 'package:google_fonts/google_fonts.dart';
|
|
||||||
// import 'package:nhancepolicy/customAppBar/toastHelper.dart';
|
|
||||||
// import 'package:nhancepolicy/models/environment.dart';
|
|
||||||
// import 'package:nhancepolicy/service/api_service.dart';
|
|
||||||
// import 'package:nhancepolicy/service/token_storage_service.dart';
|
|
||||||
// import 'package:pinput/pinput.dart';
|
|
||||||
// import 'dart:async';
|
|
||||||
// import 'package:flutter/gestures.dart';
|
|
||||||
// import 'package:http/http.dart' as http;
|
|
||||||
// import 'dart:convert';
|
|
||||||
// import 'dart:io';
|
|
||||||
// import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
// import 'package:nhancepolicy/responsive.dart';
|
|
||||||
// import 'package:jwt_decode/jwt_decode.dart';
|
|
||||||
//
|
|
||||||
// import 'branch/branch_selection_page.dart';
|
|
||||||
//
|
|
||||||
// class MyHrVerify extends StatefulWidget {
|
|
||||||
// final String verificationId;
|
|
||||||
// final String mobileNumber;
|
|
||||||
// final int? resendToken;
|
|
||||||
// final Function(String, int?) onResendCode;
|
|
||||||
//
|
|
||||||
// const MyHrVerify({
|
|
||||||
// Key? key,
|
|
||||||
// required this.verificationId,
|
|
||||||
// required this.mobileNumber,
|
|
||||||
// required this.resendToken,
|
|
||||||
// required this.onResendCode,
|
|
||||||
// }) : super(key: key);
|
|
||||||
//
|
|
||||||
// @override
|
|
||||||
// State<MyHrVerify> createState() => _MyVerifyState();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// class _MyVerifyState extends State<MyHrVerify> {
|
|
||||||
// TextEditingController countryController = TextEditingController();
|
|
||||||
// late ApiService apiService;
|
|
||||||
// TextEditingController _otpController = TextEditingController();
|
|
||||||
// final _formKey = GlobalKey<FormState>();
|
|
||||||
// late Timer _timer;
|
|
||||||
// int _secondsRemaining = 60;
|
|
||||||
// bool _isTimerRunning = false;
|
|
||||||
//
|
|
||||||
// dynamic enrollmentEmpClientBranchId;
|
|
||||||
// dynamic enrollmentEmpCodeString;
|
|
||||||
// dynamic enrollmentEmpPrimaryId;
|
|
||||||
// dynamic enrollmentGpaEmpName;
|
|
||||||
// dynamic enrollmentClient_id;
|
|
||||||
// dynamic enrollmentHrId;
|
|
||||||
// List<dynamic> enrollmentAllowed_modules = [];
|
|
||||||
// dynamic enrollmentEmp_status;
|
|
||||||
//
|
|
||||||
// dynamic empClientBranchId;
|
|
||||||
// dynamic empCodeString;
|
|
||||||
// dynamic empPrimaryId;
|
|
||||||
// dynamic gpaEmpName;
|
|
||||||
// dynamic client_id;
|
|
||||||
// dynamic empHrId;
|
|
||||||
// List<dynamic> empAllowed_modules = [];
|
|
||||||
// dynamic emp_status;
|
|
||||||
//
|
|
||||||
// dynamic _token;
|
|
||||||
// dynamic _postToken;
|
|
||||||
// dynamic clientName;
|
|
||||||
// dynamic clientLogo;
|
|
||||||
//
|
|
||||||
// dynamic fCMToken;
|
|
||||||
// late String verificationId;
|
|
||||||
// late String mobileNumber;
|
|
||||||
// final FirebaseAuth _auth = FirebaseAuth.instance;
|
|
||||||
// bool _isLoading = false;
|
|
||||||
// dynamic empMobileNo;
|
|
||||||
// dynamic empEmailid;
|
|
||||||
//
|
|
||||||
// late String _verificationId;
|
|
||||||
//
|
|
||||||
// @override
|
|
||||||
// void initState() {
|
|
||||||
// super.initState();
|
|
||||||
// countryController.text = "+91";
|
|
||||||
// apiService = ApiService(context);
|
|
||||||
// // checkTokenAvailability();
|
|
||||||
// verificationId = widget.verificationId;
|
|
||||||
// mobileNumber = widget.mobileNumber;
|
|
||||||
// logDebug('Received verificationId: $verificationId');
|
|
||||||
// logDebug('Received mobileNumber: $mobileNumber');
|
|
||||||
// if (verificationId.isEmpty) {
|
|
||||||
// // Handle the case where verificationId is not provided
|
|
||||||
// Navigator.pop(context);
|
|
||||||
// }
|
|
||||||
// startTimer();
|
|
||||||
// // Equivalent to componentDidMount
|
|
||||||
// _auth.authStateChanges().listen((User? user) {
|
|
||||||
// if (user != null) {
|
|
||||||
// // If user is already signed in, log them out
|
|
||||||
// logOutFirebase();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// void logOutFirebase() async {
|
|
||||||
// await _auth.signOut();
|
|
||||||
// logDebug('User signed out');
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @override
|
|
||||||
// void dispose() {
|
|
||||||
// _timer.cancel();
|
|
||||||
// super.dispose();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// void startTimer() {
|
|
||||||
// _isTimerRunning = true;
|
|
||||||
// _timer = Timer.periodic(Duration(seconds: 1), (Timer timer) {
|
|
||||||
// setState(() {
|
|
||||||
// if (_secondsRemaining > 0) {
|
|
||||||
// _secondsRemaining--;
|
|
||||||
// } else {
|
|
||||||
// _isTimerRunning = false;
|
|
||||||
// _timer.cancel();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// void generateToken(bool otpVerifyStatus) async {
|
|
||||||
// try {
|
|
||||||
// final response = await http.post(
|
|
||||||
// Uri.parse(Environment.apiUrl + 'getVerifiedHrData'),
|
|
||||||
// body: json.encode(
|
|
||||||
// {'mobile_no': mobileNumber, 'otp_verification': otpVerifyStatus}),
|
|
||||||
// headers: {
|
|
||||||
// HttpHeaders.contentTypeHeader: 'application/json',
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// if (response.statusCode == 200) {
|
|
||||||
// setState(() {
|
|
||||||
// _isLoading = false;
|
|
||||||
// });
|
|
||||||
// Map<String, dynamic> data = json.decode(response.body);
|
|
||||||
//
|
|
||||||
// logDebug('Response data: $data');
|
|
||||||
//
|
|
||||||
// // Extract enrollment data
|
|
||||||
// List<dynamic> preEnrollmentData = data['data'] ?? [];
|
|
||||||
// Map<String, dynamic> postEnrollment = data['post_enrollment'] ?? {};
|
|
||||||
// List<dynamic> postEnrollmentData = postEnrollment['data'] ?? [];
|
|
||||||
//
|
|
||||||
// // Save to global storage
|
|
||||||
// final tokenStorage = TokenStorageService();
|
|
||||||
// await tokenStorage.saveEnrollmentData(
|
|
||||||
// preEnrollmentData,
|
|
||||||
// postEnrollmentData,
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// // Navigate to branch selection
|
|
||||||
// Navigator.pushReplacement(
|
|
||||||
// context,
|
|
||||||
// MaterialPageRoute(
|
|
||||||
// builder: (context) => BranchSelectionPage(),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// // logDebug('data: $data');
|
|
||||||
// // _token = data['data'];
|
|
||||||
// // String status = data['status'];
|
|
||||||
// //
|
|
||||||
// // // Directly access the post_enrollment data
|
|
||||||
// // Map<String, dynamic> post = data['post_enrollment'];
|
|
||||||
// // logDebug('post: $post');
|
|
||||||
// //
|
|
||||||
// // _postToken = post['data'];
|
|
||||||
// // String postStatus = post['status'];
|
|
||||||
// //
|
|
||||||
// // if (postStatus == 'success') {
|
|
||||||
// // setState(() {
|
|
||||||
// // _isLoading = false;
|
|
||||||
// // });
|
|
||||||
// // postSuccessData(post, data);
|
|
||||||
// // } else if (status == 'success') {
|
|
||||||
// // setState(() {
|
|
||||||
// // _isLoading = false;
|
|
||||||
// // });
|
|
||||||
// // enrollmentSuccessData(data);
|
|
||||||
// // } else {
|
|
||||||
// // setState(() {
|
|
||||||
// // _isLoading = false;
|
|
||||||
// // });
|
|
||||||
// // ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again');
|
|
||||||
// // // Show a Snackbar if the OTP is invalid
|
|
||||||
// // logDebug('Invalid OTP. Please try again');
|
|
||||||
// // }
|
|
||||||
// } else {
|
|
||||||
// setState(() {
|
|
||||||
// _isLoading = false;
|
|
||||||
// });
|
|
||||||
// ToastHelper.showWarningToast(context, 'Something went wrong');
|
|
||||||
// throw Exception('Failed to verify OTP');
|
|
||||||
// }
|
|
||||||
// } catch (e) {
|
|
||||||
// setState(() {
|
|
||||||
// _isLoading = false;
|
|
||||||
// });
|
|
||||||
// logDebug('Error: $e');
|
|
||||||
// ToastHelper.showWarningToast(context, 'Something went wrong');
|
|
||||||
// // Show a Snackbar if there's an error while verifying OTP
|
|
||||||
// logDebug('Failed to verify OTP. Please try again.');
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// void postSuccessData(post, data) async {
|
|
||||||
// String status = data['status'];
|
|
||||||
// String postStatus = post['status'];
|
|
||||||
//
|
|
||||||
// if (postStatus == 'success') {
|
|
||||||
// final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
// prefs.setString('_postToken', post['data']);
|
|
||||||
//
|
|
||||||
// // Decode the JWT token received from the API response
|
|
||||||
// Map<String, dynamic>? decodedToken = Jwt.parseJwt(post['data']);
|
|
||||||
// logDebug('postdecodedToken : $decodedToken');
|
|
||||||
// empClientBranchId = decodedToken['ref_id'];
|
|
||||||
// prefs.setString('empClientBranchId', empClientBranchId);
|
|
||||||
// empCodeString = decodedToken['emp_code'].toString();
|
|
||||||
// prefs.setString('empCode', empCodeString);
|
|
||||||
// empPrimaryId = decodedToken['id'];
|
|
||||||
// prefs.setString('empPrimaryId', empPrimaryId);
|
|
||||||
// gpaEmpName = decodedToken['name'].toString();
|
|
||||||
// prefs.setString('gpaEmpName', gpaEmpName);
|
|
||||||
// client_id = decodedToken['client_id'];
|
|
||||||
// prefs.setString('client_id', client_id);
|
|
||||||
// empHrId = decodedToken['id'];
|
|
||||||
// prefs.setString('empHrId', empHrId);
|
|
||||||
// empAllowed_modules = decodedToken['allowed_modules'];
|
|
||||||
// prefs.setString('empAllowed_modules', jsonEncode(empAllowed_modules));
|
|
||||||
// emp_status = decodedToken['emp_status'];
|
|
||||||
// prefs.setString('emp_status', emp_status);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (status == 'success') {
|
|
||||||
// final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
// prefs.setString('enrollToken', data['data']);
|
|
||||||
//
|
|
||||||
// // Decode the JWT token received from the API response
|
|
||||||
// Map<String, dynamic>? decodedToken = Jwt.parseJwt(data['data']);
|
|
||||||
// logDebug('decodedToken : $decodedToken');
|
|
||||||
// enrollmentEmpClientBranchId = decodedToken['ref_id'];
|
|
||||||
// prefs.setString(
|
|
||||||
// 'enrollmentEmpClientBranchId', enrollmentEmpClientBranchId);
|
|
||||||
// enrollmentEmpCodeString = decodedToken['emp_code'].toString();
|
|
||||||
// prefs.setString('enrollmentEmpCodeString', enrollmentEmpCodeString);
|
|
||||||
// enrollmentEmpPrimaryId = decodedToken['id'];
|
|
||||||
// prefs.setString('enrollmentEmpPrimaryId', enrollmentEmpPrimaryId);
|
|
||||||
// enrollmentGpaEmpName = decodedToken['name'].toString();
|
|
||||||
// prefs.setString('enrollmentGpaEmpName', enrollmentGpaEmpName);
|
|
||||||
// enrollmentClient_id = decodedToken['client_id'];
|
|
||||||
// prefs.setString('enrollmentClient_id', enrollmentClient_id);
|
|
||||||
//
|
|
||||||
// enrollmentHrId = decodedToken['id'];
|
|
||||||
// prefs.setString('enrollmentHrId', enrollmentHrId);
|
|
||||||
// enrollmentAllowed_modules = decodedToken['allowed_modules'];
|
|
||||||
// prefs.setString(
|
|
||||||
// 'enrollmentAllowed_modules', jsonEncode(enrollmentAllowed_modules));
|
|
||||||
// // enrollmentEmp_status = decodedToken['emp_status'];
|
|
||||||
// // prefs.setString('enrollmentEmp_status', enrollmentEmp_status);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
// final _postToken = prefs.getString('_postToken');
|
|
||||||
//
|
|
||||||
// if (_postToken != null && _postToken.isNotEmpty) {
|
|
||||||
// ToastHelper.showSuccessToast(context, 'Successfully Login');
|
|
||||||
// // if (emp_status == 'enrolled' || emp_status == 'active') {
|
|
||||||
// Navigator.pushReplacementNamed(context, 'hrDashboard');
|
|
||||||
// // } else {
|
|
||||||
// // Navigator.pushReplacementNamed(context, 'empDetails');
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// void enrollmentSuccessData(data) async {
|
|
||||||
// final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
// prefs.setString('enrollToken', data['data']);
|
|
||||||
//
|
|
||||||
// // Decode the JWT token received from the API response
|
|
||||||
// Map<String, dynamic>? decodedToken = Jwt.parseJwt(data['data']);
|
|
||||||
// logDebug('enrolldecodedToken : $decodedToken');
|
|
||||||
// enrollmentEmpClientBranchId = decodedToken['ref_id'];
|
|
||||||
// prefs.setString('enrollmentEmpClientBranchId', enrollmentEmpClientBranchId);
|
|
||||||
// enrollmentEmpCodeString = decodedToken['emp_code'].toString();
|
|
||||||
// prefs.setString('enrollmentEmpCodeString', enrollmentEmpCodeString);
|
|
||||||
// enrollmentEmpPrimaryId = decodedToken['id'];
|
|
||||||
// prefs.setString('enrollmentEmpPrimaryId', enrollmentEmpPrimaryId);
|
|
||||||
// enrollmentGpaEmpName = decodedToken['name'].toString();
|
|
||||||
// prefs.setString('enrollmentGpaEmpName', enrollmentGpaEmpName);
|
|
||||||
// enrollmentClient_id = decodedToken['client_id'];
|
|
||||||
// prefs.setString('enrollmentClient_id', enrollmentClient_id);
|
|
||||||
//
|
|
||||||
// enrollmentHrId = decodedToken['id'];
|
|
||||||
// prefs.setString('enrollmentHrId', enrollmentHrId);
|
|
||||||
// enrollmentAllowed_modules = decodedToken['allowed_modules'];
|
|
||||||
// prefs.setString(
|
|
||||||
// 'enrollmentAllowed_modules', jsonEncode(enrollmentAllowed_modules));
|
|
||||||
// // enrollmentEmp_status = decodedToken['emp_status'];
|
|
||||||
// // prefs.setString('enrollmentEmp_status', enrollmentEmp_status);
|
|
||||||
//
|
|
||||||
// logDebug('Successfully Login');
|
|
||||||
//
|
|
||||||
// // Redirect to another page
|
|
||||||
// final enrollToken = prefs.getString('enrollToken');
|
|
||||||
//
|
|
||||||
// if (enrollToken != null && enrollToken.isNotEmpty) {
|
|
||||||
// ToastHelper.showSuccessToast(context, 'Successfully Login');
|
|
||||||
// // if (emp_status == 'enrolled' || emp_status == 'active') {
|
|
||||||
// // Navigator.pushReplacementNamed(context, 'home');
|
|
||||||
// // } else {
|
|
||||||
// Navigator.pushReplacementNamed(context, 'hrDashboard');
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// void verifyOTP(String otp) async {
|
|
||||||
// try {
|
|
||||||
// // setState(() {
|
|
||||||
// // _isLoading = true;
|
|
||||||
// // });
|
|
||||||
// // Later, in verify screen:
|
|
||||||
// SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
// dynamic verificationId = prefs.getString('verificationId');
|
|
||||||
//
|
|
||||||
// final credential = PhoneAuthProvider.credential(
|
|
||||||
// verificationId: verificationId, // Make sure this is up-to-date
|
|
||||||
// smsCode: otp,
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// // final userCredential = await FirebaseAuth.instance.signInWithCredential(credential);
|
|
||||||
// // await FirebaseAuth.instance.signInWithCredential(credential);
|
|
||||||
//
|
|
||||||
// // if (userCredential.user != null) {
|
|
||||||
// bool otpVerifyStatus = true;
|
|
||||||
// generateToken(otpVerifyStatus);
|
|
||||||
// // } else {
|
|
||||||
// // ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again.');
|
|
||||||
// // }
|
|
||||||
//
|
|
||||||
// // await FirebaseAuth.instance.signInWithCredential(credential).then((userCredential) async {
|
|
||||||
// // if (userCredential.user != null) {
|
|
||||||
// // bool otpVerifyStatus = true;
|
|
||||||
// // generateToken(otpVerifyStatus);
|
|
||||||
// // } else {
|
|
||||||
// // setState(() {
|
|
||||||
// // _isLoading = false;
|
|
||||||
// // });
|
|
||||||
// // ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again');
|
|
||||||
// // }
|
|
||||||
// // });
|
|
||||||
// } on FirebaseAuthException catch (e) {
|
|
||||||
// if (e.code == 'code-expired') {
|
|
||||||
// ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
// SnackBar(
|
|
||||||
// content: Text('OTP expired. Please request a new one.'),
|
|
||||||
// duration: Duration(seconds: 2),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// ToastHelper.showErrorToast(
|
|
||||||
// context, 'OTP expired. Please request a new one.');
|
|
||||||
// } else if (e.code == 'invalid-verification-code') {
|
|
||||||
// ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
// SnackBar(
|
|
||||||
// content: Text('Invalid OTP entered. Please try again.'),
|
|
||||||
// duration: Duration(seconds: 2),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// ToastHelper.showErrorToast(
|
|
||||||
// context, 'Invalid OTP entered. Please try again.');
|
|
||||||
// } else if (e.code == 'session-expired') {
|
|
||||||
// ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
// SnackBar(
|
|
||||||
// content: Text('Session expired. Try restarting the verification.'),
|
|
||||||
// duration: Duration(seconds: 2),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// ToastHelper.showErrorToast(
|
|
||||||
// context, 'Session expired. Try restarting the verification.');
|
|
||||||
// } else {
|
|
||||||
// ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
// SnackBar(
|
|
||||||
// content: Text('Error: ${e.message}'),
|
|
||||||
// duration: Duration(seconds: 2),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// ToastHelper.showErrorToast(context, 'Error: ${e.message}');
|
|
||||||
// }
|
|
||||||
// } catch (e) {
|
|
||||||
// // setState(() {
|
|
||||||
// // _isLoading = false;
|
|
||||||
// // });
|
|
||||||
// logDebug('Error: $e');
|
|
||||||
// ToastHelper.showErrorToast(
|
|
||||||
// context, 'Failed to verify OTP. Please try again.');
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// void _resendOTP() {
|
|
||||||
// setState(() {
|
|
||||||
// _secondsRemaining = 60;
|
|
||||||
// _isTimerRunning = true;
|
|
||||||
// });
|
|
||||||
// startTimer();
|
|
||||||
// widget.onResendCode(widget.mobileNumber, widget.resendToken);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @override
|
|
||||||
// Widget build(BuildContext context) {
|
|
||||||
// // Retrieve the passed mobile number value
|
|
||||||
// // final String mobileNumber =
|
|
||||||
// // ModalRoute.of(context)!.settings.arguments as String;
|
|
||||||
// Size _size = MediaQuery.of(context).size;
|
|
||||||
// EdgeInsets marginInsets = EdgeInsets.zero;
|
|
||||||
// if (Responsive.isDesktop(context)) {
|
|
||||||
// marginInsets = const EdgeInsets.only(
|
|
||||||
// left: 0,
|
|
||||||
// right: 0,
|
|
||||||
// bottom: 0,
|
|
||||||
// top: 0,
|
|
||||||
// );
|
|
||||||
// } else if (Responsive.isMobile(context)) {
|
|
||||||
// marginInsets = const EdgeInsets.only(
|
|
||||||
// left: 25, // Example value for mobile
|
|
||||||
// right: 25, // Example value for mobile
|
|
||||||
// bottom: 0, // Example value for mobile
|
|
||||||
// top: 0, // Example value for mobile
|
|
||||||
// );
|
|
||||||
// } else if (Responsive.isTablet(context)) {
|
|
||||||
// marginInsets = const EdgeInsets.only(
|
|
||||||
// left: 25, // Example value for mobile
|
|
||||||
// right: 25, // Example value for mobile
|
|
||||||
// bottom: 0, // Example value for mobile
|
|
||||||
// top: 0, // Example value for mobile
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// final defaultPinTheme = PinTheme(
|
|
||||||
// width: 56,
|
|
||||||
// height: 56,
|
|
||||||
// textStyle: TextStyle(
|
|
||||||
// fontSize: 20,
|
|
||||||
// color: Color.fromRGBO(30, 60, 87, 1),
|
|
||||||
// fontWeight: FontWeight.w600,
|
|
||||||
// ),
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// border: Border.all(color: Color.fromRGBO(234, 239, 243, 1)),
|
|
||||||
// borderRadius: BorderRadius.circular(20),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// final focusedPinTheme = defaultPinTheme.copyDecorationWith(
|
|
||||||
// border: Border.all(color: Color.fromRGBO(114, 178, 238, 1)),
|
|
||||||
// borderRadius: BorderRadius.circular(8),
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// final submittedPinTheme = defaultPinTheme.copyWith(
|
|
||||||
// decoration: defaultPinTheme.decoration?.copyWith(
|
|
||||||
// color: Color.fromRGBO(234, 239, 243, 1),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// return Scaffold(
|
|
||||||
// body: SingleChildScrollView(
|
|
||||||
// keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
|
|
||||||
// child: Container(
|
|
||||||
// height: _size.height,
|
|
||||||
// color: Colors.white,
|
|
||||||
// child: Stack(
|
|
||||||
// children: [
|
|
||||||
// Visibility(
|
|
||||||
// visible: _size.width <= 1100,
|
|
||||||
// child: ClipRRect(
|
|
||||||
// borderRadius: BorderRadius.only(
|
|
||||||
// bottomLeft: Radius.circular(30),
|
|
||||||
// bottomRight: Radius.circular(30),
|
|
||||||
// ),
|
|
||||||
// child: Container(
|
|
||||||
// height: _size.height / 3,
|
|
||||||
// width: double.infinity,
|
|
||||||
// color: Color(0xFF00989E),
|
|
||||||
// child: Stack(
|
|
||||||
// children: [
|
|
||||||
// Column(
|
|
||||||
// children: [
|
|
||||||
// SizedBox(
|
|
||||||
// height: _size.height /
|
|
||||||
// 6.4), // Adjust the spacing between the rows
|
|
||||||
// Row(
|
|
||||||
// mainAxisAlignment: MainAxisAlignment
|
|
||||||
// .center, // Align to the center
|
|
||||||
// children: [
|
|
||||||
// Expanded(
|
|
||||||
// flex: Responsive.isDesktop(context) ? 10 : 12,
|
|
||||||
// child: Align(
|
|
||||||
// alignment: Responsive.isDesktop(context)
|
|
||||||
// ? Alignment.centerLeft
|
|
||||||
// : Alignment.bottomCenter,
|
|
||||||
// child: Image.asset(
|
|
||||||
// _size.width <= 1100
|
|
||||||
// ? 'assets/mobileViewLogo.png'
|
|
||||||
// : 'assets/Nhance-Logo-Final 1.png',
|
|
||||||
// width: 150,
|
|
||||||
// height: 150,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// if (!Responsive.isMobile(context) &&
|
|
||||||
// !Responsive.isTablet(context))
|
|
||||||
// Expanded(
|
|
||||||
// flex: 2,
|
|
||||||
// child: MouseRegion(
|
|
||||||
// cursor: SystemMouseCursors.click,
|
|
||||||
// child: GestureDetector(
|
|
||||||
// onTap: () {
|
|
||||||
// // Add your navigation logic here
|
|
||||||
// // For example, you can use Navigator.push to navigate to another page
|
|
||||||
// Navigator.pushNamed(
|
|
||||||
// context, 'hrLogin');
|
|
||||||
// },
|
|
||||||
// child: Row(
|
|
||||||
// mainAxisAlignment: MainAxisAlignment
|
|
||||||
// .end, // Align to the end (right)
|
|
||||||
// children: [
|
|
||||||
// Text(
|
|
||||||
// 'HR Login',
|
|
||||||
// style: TextStyle(
|
|
||||||
// color: Color(
|
|
||||||
// 0xFF000000), // Text color
|
|
||||||
// // Add other text styles as needed
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// SizedBox(width: 5),
|
|
||||||
// Icon(
|
|
||||||
// Icons
|
|
||||||
// .east, // Icon for customer login
|
|
||||||
// color: Colors
|
|
||||||
// .black, // Adjust color as needed
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// Container(
|
|
||||||
// margin: marginInsets,
|
|
||||||
// alignment: Alignment.bottomCenter,
|
|
||||||
// child: SingleChildScrollView(
|
|
||||||
// child: Form(
|
|
||||||
// key: _formKey,
|
|
||||||
// child: Column(
|
|
||||||
// children: [
|
|
||||||
// Row(
|
|
||||||
// children: [
|
|
||||||
// if (_size.width > 1100)
|
|
||||||
// Expanded(
|
|
||||||
// flex: _size.width < 1100 ? 6 : 12,
|
|
||||||
// child: LayoutBuilder(
|
|
||||||
// builder: (BuildContext context,
|
|
||||||
// BoxConstraints constraints) {
|
|
||||||
// if (constraints.maxWidth > 600) {
|
|
||||||
// return Image.asset(
|
|
||||||
// 'assets/hrLogin.jpg',
|
|
||||||
// height: _size.height,
|
|
||||||
// fit: BoxFit.cover,
|
|
||||||
// );
|
|
||||||
// } else {
|
|
||||||
// return SizedBox();
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// Expanded(
|
|
||||||
// flex: _size.width < 1100 ? 6 : 12,
|
|
||||||
// child: Container(
|
|
||||||
// margin: _size.width > 1100
|
|
||||||
// ? EdgeInsets.only(left: 20, right: 20)
|
|
||||||
// : EdgeInsets.only(left: 0, right: 0),
|
|
||||||
// child: Column(
|
|
||||||
// mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
// children: [
|
|
||||||
// if (!Responsive.isMobile(context) &&
|
|
||||||
// !Responsive.isTablet(context))
|
|
||||||
// Row(
|
|
||||||
// children: [
|
|
||||||
// Expanded(
|
|
||||||
// flex: 8,
|
|
||||||
// child: Align(
|
|
||||||
// alignment: Alignment.center, // Align to the start
|
|
||||||
// child: _size.width <= 1100
|
|
||||||
// ? Image.asset(
|
|
||||||
// 'assets/Nhance-Logo-Final-mobile.png',
|
|
||||||
// width: 150,
|
|
||||||
// height: 70,
|
|
||||||
// )
|
|
||||||
// : _size.width > 1100
|
|
||||||
// ? Image.asset(
|
|
||||||
// 'assets/Nhance-Logo-Final 1.png',
|
|
||||||
// width: 150,
|
|
||||||
// height: 70,
|
|
||||||
// )
|
|
||||||
// : Image.asset(
|
|
||||||
// 'assets/Nhance-Logo-Final 1.png',
|
|
||||||
// width: 150,
|
|
||||||
// height: 70,
|
|
||||||
// ),
|
|
||||||
// )),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 80),
|
|
||||||
// Container(
|
|
||||||
// margin: Responsive.isDesktop(context)
|
|
||||||
// ? EdgeInsets.symmetric(
|
|
||||||
// horizontal: 150)
|
|
||||||
// : EdgeInsets.symmetric(horizontal: 0),
|
|
||||||
// child: Row(
|
|
||||||
// mainAxisAlignment:
|
|
||||||
// MainAxisAlignment.center,
|
|
||||||
// children: [
|
|
||||||
// Text(
|
|
||||||
// "Welcome to Nhance",
|
|
||||||
// style: GoogleFonts.poppins(
|
|
||||||
// fontSize: 16,
|
|
||||||
// fontWeight: FontWeight.bold,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 10),
|
|
||||||
// Container(
|
|
||||||
// margin: Responsive.isDesktop(context)
|
|
||||||
// ? EdgeInsets.symmetric(
|
|
||||||
// horizontal: 150)
|
|
||||||
// : EdgeInsets.symmetric(horizontal: 0),
|
|
||||||
// child: RichText(
|
|
||||||
// textAlign: TextAlign.center,
|
|
||||||
// text: TextSpan(
|
|
||||||
// text:
|
|
||||||
// "Please enter the one-time usage code sent to your mobile number $mobileNumber",
|
|
||||||
// style: GoogleFonts.poppins(
|
|
||||||
// fontSize: 12,
|
|
||||||
// height: 1.5,
|
|
||||||
// color: Color(0xFF000000)),
|
|
||||||
// children: [
|
|
||||||
// TextSpan(
|
|
||||||
// text: " (Change Number)",
|
|
||||||
// style: GoogleFonts.poppins(
|
|
||||||
// fontSize: 12,
|
|
||||||
// color: Color(
|
|
||||||
// 0xFFE26728)), // Change color as desired
|
|
||||||
// recognizer: TapGestureRecognizer()
|
|
||||||
// ..onTap = () {
|
|
||||||
// // Navigate to the page where the user can change the phone number
|
|
||||||
// Navigator.pushNamed(
|
|
||||||
// context, 'hrLogin');
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 15),
|
|
||||||
// Container(
|
|
||||||
// margin: Responsive.isDesktop(context)
|
|
||||||
// ? EdgeInsets.symmetric(
|
|
||||||
// horizontal: 150)
|
|
||||||
// : EdgeInsets.symmetric(horizontal: 0),
|
|
||||||
// child: Pinput(
|
|
||||||
// length: 6,
|
|
||||||
// // defaultPinTheme: defaultPinTheme,
|
|
||||||
// // focusedPinTheme: focusedPinTheme,
|
|
||||||
// // submittedPinTheme: submittedPinTheme,
|
|
||||||
// showCursor: true,
|
|
||||||
// controller: _otpController,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 10),
|
|
||||||
// Container(
|
|
||||||
// margin: Responsive.isDesktop(context)
|
|
||||||
// ? EdgeInsets.symmetric(
|
|
||||||
// horizontal: 150)
|
|
||||||
// : EdgeInsets.symmetric(horizontal: 0),
|
|
||||||
// child: Row(
|
|
||||||
// mainAxisAlignment: MainAxisAlignment
|
|
||||||
// .end, // Align text to the right
|
|
||||||
// children: [
|
|
||||||
// _isTimerRunning
|
|
||||||
// ? Text(
|
|
||||||
// "Resend OTP in $_secondsRemaining seconds",
|
|
||||||
// style: GoogleFonts.poppins(
|
|
||||||
// color: Colors.black),
|
|
||||||
// )
|
|
||||||
// : InkWell(
|
|
||||||
// onTap: () {
|
|
||||||
// _resendOTP();
|
|
||||||
// },
|
|
||||||
// child: Text(
|
|
||||||
// "Resend OTP",
|
|
||||||
// style: GoogleFonts.poppins(
|
|
||||||
// color: Colors.blue),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 10),
|
|
||||||
// Container(
|
|
||||||
// margin: Responsive.isDesktop(context)
|
|
||||||
// ? EdgeInsets.symmetric(
|
|
||||||
// horizontal: 150)
|
|
||||||
// : EdgeInsets.symmetric(horizontal: 0),
|
|
||||||
// child: SizedBox(
|
|
||||||
// width: double.infinity,
|
|
||||||
// height: 45,
|
|
||||||
// child: ElevatedButton(
|
|
||||||
// style: ElevatedButton.styleFrom(
|
|
||||||
// backgroundColor: Color(0xFF00989E),
|
|
||||||
// shape: RoundedRectangleBorder(
|
|
||||||
// borderRadius:
|
|
||||||
// BorderRadius.circular(10),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// onPressed: () {
|
|
||||||
// if (_formKey.currentState!
|
|
||||||
// .validate()) {
|
|
||||||
// _formKey.currentState!
|
|
||||||
// .save(); // Save form fields before calling verifyOTP
|
|
||||||
// verifyOTP(_otpController.text);
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// child: Text(
|
|
||||||
// "Submit",
|
|
||||||
// style: GoogleFonts.poppins(
|
|
||||||
// color: Color(0xFFFFFFFF)),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// SizedBox(
|
|
||||||
// height: _size.width <= 1100 ? 0 : 0,
|
|
||||||
// ),
|
|
||||||
// // _size.width > 1100
|
|
||||||
// // ? Container(
|
|
||||||
// // margin: EdgeInsets.symmetric(
|
|
||||||
// // horizontal: 150),
|
|
||||||
// // child: Column(
|
|
||||||
// // children: [
|
|
||||||
// // SizedBox(height: 30),
|
|
||||||
// // Text(
|
|
||||||
// // "Benefits of Login",
|
|
||||||
// // style: GoogleFonts.poppins(
|
|
||||||
// // fontSize: 20,
|
|
||||||
// // fontWeight: FontWeight.bold,
|
|
||||||
// // ),
|
|
||||||
// // ),
|
|
||||||
// // SizedBox(height: 15),
|
|
||||||
// // ],
|
|
||||||
// // ))
|
|
||||||
// // : SizedBox(),
|
|
||||||
// // _size.width > 1100
|
|
||||||
// // ? Container(
|
|
||||||
// // margin: EdgeInsets.symmetric(
|
|
||||||
// // horizontal: 150),
|
|
||||||
// // child: Row(
|
|
||||||
// // mainAxisAlignment:
|
|
||||||
// // MainAxisAlignment.center,
|
|
||||||
// // children: [
|
|
||||||
// // Expanded(
|
|
||||||
// // flex: 6,
|
|
||||||
// // child: Container(
|
|
||||||
// // padding:
|
|
||||||
// // EdgeInsets.symmetric(
|
|
||||||
// // vertical: 8),
|
|
||||||
// // child: Row(
|
|
||||||
// // mainAxisAlignment:
|
|
||||||
// // MainAxisAlignment
|
|
||||||
// // .center,
|
|
||||||
// // children: [
|
|
||||||
// // Expanded(
|
|
||||||
// // child: Container(
|
|
||||||
// // padding: EdgeInsets
|
|
||||||
// // .symmetric(
|
|
||||||
// // vertical:
|
|
||||||
// // 12),
|
|
||||||
// // decoration:
|
|
||||||
// // BoxDecoration(
|
|
||||||
// // border: Border(
|
|
||||||
// // right:
|
|
||||||
// // BorderSide(
|
|
||||||
// // width: 1,
|
|
||||||
// // color: Colors
|
|
||||||
// // .black,
|
|
||||||
// // ),
|
|
||||||
// // ),
|
|
||||||
// // ),
|
|
||||||
// // child: Column(
|
|
||||||
// // children: [
|
|
||||||
// // Icon(
|
|
||||||
// // Icons
|
|
||||||
// // .policy,
|
|
||||||
// // color: Color(
|
|
||||||
// // 0xFFE26728)),
|
|
||||||
// // SizedBox(
|
|
||||||
// // height: 10),
|
|
||||||
// // Text(
|
|
||||||
// // "View Policy"),
|
|
||||||
// // ],
|
|
||||||
// // ),
|
|
||||||
// // ),
|
|
||||||
// // ),
|
|
||||||
// // Expanded(
|
|
||||||
// // child: Container(
|
|
||||||
// // padding: EdgeInsets
|
|
||||||
// // .symmetric(
|
|
||||||
// // vertical:
|
|
||||||
// // 12),
|
|
||||||
// // child: Column(
|
|
||||||
// // children: [
|
|
||||||
// // Icon(Icons.edit,
|
|
||||||
// // color: Color(
|
|
||||||
// // 0xFFE26728)),
|
|
||||||
// // SizedBox(
|
|
||||||
// // height: 10),
|
|
||||||
// // Text(
|
|
||||||
// // "Manage Claims"),
|
|
||||||
// // ],
|
|
||||||
// // ),
|
|
||||||
// // ),
|
|
||||||
// // ),
|
|
||||||
// // ],
|
|
||||||
// // ),
|
|
||||||
// // ),
|
|
||||||
// // ),
|
|
||||||
// // ],
|
|
||||||
// // ),
|
|
||||||
// // )
|
|
||||||
// // : SizedBox(
|
|
||||||
// // height:
|
|
||||||
// // Responsive.isDesktop(context)
|
|
||||||
// // ? _size.height * 0.1
|
|
||||||
// // : _size.height * 0.2,
|
|
||||||
// // ),
|
|
||||||
// SizedBox(
|
|
||||||
// height: Responsive.isDesktop(context)
|
|
||||||
// ? _size.height * 0.3
|
|
||||||
// : _size.height * 0.2,
|
|
||||||
// ),
|
|
||||||
// // SizedBox(
|
|
||||||
// // height: _size.height * 0.1,
|
|
||||||
// // ),
|
|
||||||
// Align(
|
|
||||||
// alignment: Alignment.bottomCenter,
|
|
||||||
// child: Container(
|
|
||||||
// width: double
|
|
||||||
// .infinity, // Make the footer full width
|
|
||||||
// child: Container(
|
|
||||||
// alignment: Alignment.bottomCenter,
|
|
||||||
// padding:
|
|
||||||
// EdgeInsets.symmetric(vertical: 8),
|
|
||||||
// child: RichText(
|
|
||||||
// textAlign: TextAlign.center,
|
|
||||||
// text: TextSpan(
|
|
||||||
// text:
|
|
||||||
// 'By continuing, you agree with our ',
|
|
||||||
// style: GoogleFonts.poppins(
|
|
||||||
// color: Colors.black,
|
|
||||||
// fontSize: 9,
|
|
||||||
// ),
|
|
||||||
// children: <TextSpan>[
|
|
||||||
// TextSpan(
|
|
||||||
// text: 'privacy policy ',
|
|
||||||
// style: GoogleFonts.poppins(
|
|
||||||
// color: Color(0xFF00989E),
|
|
||||||
// fontSize: 9,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// TextSpan(
|
|
||||||
// text: 'and ',
|
|
||||||
// style: GoogleFonts.poppins(
|
|
||||||
// color: Colors.black,
|
|
||||||
// fontSize: 9,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// TextSpan(
|
|
||||||
// text: 'terms of use',
|
|
||||||
// style: GoogleFonts.poppins(
|
|
||||||
// color: Color(0xFF00989E),
|
|
||||||
// fontSize: 9,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// )),
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
111
lib/logger.dart
@ -1,111 +0,0 @@
|
|||||||
import 'dart:developer' as developer;
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:nhancepolicy/config/environment.dart';
|
|
||||||
|
|
||||||
String _formatMessage(
|
|
||||||
String message, {
|
|
||||||
String? className,
|
|
||||||
String? methodName,
|
|
||||||
}) {
|
|
||||||
final timestamp = DateTime.now().toIso8601String();
|
|
||||||
final contextParts = <String>[];
|
|
||||||
if (className != null && className.isNotEmpty) {
|
|
||||||
contextParts.add(className);
|
|
||||||
}
|
|
||||||
if (methodName != null && methodName.isNotEmpty) {
|
|
||||||
contextParts.add(methodName);
|
|
||||||
}
|
|
||||||
final context = contextParts.isEmpty ? '' : '[${contextParts.join('.')}] ';
|
|
||||||
return '[$timestamp] $context$message';
|
|
||||||
}
|
|
||||||
|
|
||||||
void _log(
|
|
||||||
String level,
|
|
||||||
String message, {
|
|
||||||
String tag = 'APP',
|
|
||||||
Object? error,
|
|
||||||
StackTrace? stackTrace,
|
|
||||||
String? className,
|
|
||||||
String? methodName,
|
|
||||||
}) {
|
|
||||||
final shouldLog = kDebugMode || Environment.flavor == Flavor.dev;
|
|
||||||
if (!shouldLog) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final formatted =
|
|
||||||
_formatMessage(message, className: className, methodName: methodName);
|
|
||||||
final channel = '$tag:$level';
|
|
||||||
|
|
||||||
developer.log(
|
|
||||||
formatted,
|
|
||||||
name: channel,
|
|
||||||
error: error,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
);
|
|
||||||
debugPrint('[$channel] $formatted');
|
|
||||||
}
|
|
||||||
|
|
||||||
void logDebug(
|
|
||||||
Object? message, {
|
|
||||||
String tag = 'APP',
|
|
||||||
String? className,
|
|
||||||
String? methodName,
|
|
||||||
}) {
|
|
||||||
_log(
|
|
||||||
'DEBUG',
|
|
||||||
'${message ?? 'null'}',
|
|
||||||
tag: tag,
|
|
||||||
className: className,
|
|
||||||
methodName: methodName,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void logInfo(
|
|
||||||
Object? message, {
|
|
||||||
String tag = 'APP',
|
|
||||||
String? className,
|
|
||||||
String? methodName,
|
|
||||||
}) {
|
|
||||||
_log(
|
|
||||||
'INFO',
|
|
||||||
'${message ?? 'null'}',
|
|
||||||
tag: tag,
|
|
||||||
className: className,
|
|
||||||
methodName: methodName,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void logWarning(
|
|
||||||
Object? message, {
|
|
||||||
String tag = 'APP',
|
|
||||||
String? className,
|
|
||||||
String? methodName,
|
|
||||||
}) {
|
|
||||||
_log(
|
|
||||||
'WARN',
|
|
||||||
'${message ?? 'null'}',
|
|
||||||
tag: tag,
|
|
||||||
className: className,
|
|
||||||
methodName: methodName,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void logError(
|
|
||||||
Object? message, {
|
|
||||||
String tag = 'APP',
|
|
||||||
Object? error,
|
|
||||||
StackTrace? stackTrace,
|
|
||||||
String? className,
|
|
||||||
String? methodName,
|
|
||||||
}) {
|
|
||||||
_log(
|
|
||||||
'ERROR',
|
|
||||||
'${message ?? 'null'}',
|
|
||||||
tag: tag,
|
|
||||||
error: error,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
className: className,
|
|
||||||
methodName: methodName,
|
|
||||||
);
|
|
||||||
}
|
|
||||||