commit e3073d982329734445f1a2892a918582abb015ba Author: Sas Andy Date: Tue Oct 1 09:37:38 2024 +0700 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..29a3a50 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# 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 diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..90eabcf --- /dev/null +++ b/.metadata @@ -0,0 +1,45 @@ +# 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: "80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + - platform: android + create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + - platform: ios + create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + - platform: linux + create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + - platform: macos + create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + - platform: web + create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + - platform: windows + create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + + # 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' diff --git a/README.md b/README.md new file mode 100644 index 0000000..91b47c4 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# mitra_corporate + +A new Flutter project. + +## Build +```bash +flutter build web --base-href "/mitra/" +``` \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..f9b3034 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1 @@ +include: package:flutter_lints/flutter.yaml diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..55afd91 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..1a6bc5f --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,44 @@ +plugins { + id "com.android.application" + id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id "dev.flutter.flutter-gradle-plugin" +} + +android { + namespace = "com.example.mitra_corporate" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.mitra_corporate" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + 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 = "../.." +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..eea64d8 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/example/mitra_corporate/MainActivity.kt b/android/app/src/main/kotlin/com/example/mitra_corporate/MainActivity.kt new file mode 100644 index 0000000..bf85918 --- /dev/null +++ b/android/app/src/main/kotlin/com/example/mitra_corporate/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.mitra_corporate + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/android/app/src/main/res/drawable-night-v21/background.png b/android/app/src/main/res/drawable-night-v21/background.png new file mode 100644 index 0000000..71e9c81 Binary files /dev/null and b/android/app/src/main/res/drawable-night-v21/background.png differ diff --git a/android/app/src/main/res/drawable-night-v21/launch_background.xml b/android/app/src/main/res/drawable-night-v21/launch_background.xml new file mode 100644 index 0000000..f88598c --- /dev/null +++ b/android/app/src/main/res/drawable-night-v21/launch_background.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/android/app/src/main/res/drawable-night/background.png b/android/app/src/main/res/drawable-night/background.png new file mode 100644 index 0000000..71e9c81 Binary files /dev/null and b/android/app/src/main/res/drawable-night/background.png differ diff --git a/android/app/src/main/res/drawable-night/launch_background.xml b/android/app/src/main/res/drawable-night/launch_background.xml new file mode 100644 index 0000000..f88598c --- /dev/null +++ b/android/app/src/main/res/drawable-night/launch_background.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/android/app/src/main/res/drawable-v21/background.png b/android/app/src/main/res/drawable-v21/background.png new file mode 100644 index 0000000..3107d37 Binary files /dev/null and b/android/app/src/main/res/drawable-v21/background.png differ diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f88598c --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/android/app/src/main/res/drawable/background.png b/android/app/src/main/res/drawable/background.png new file mode 100644 index 0000000..3107d37 Binary files /dev/null and b/android/app/src/main/res/drawable/background.png differ diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..f88598c --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/values-night-v31/styles.xml b/android/app/src/main/res/values-night-v31/styles.xml new file mode 100644 index 0000000..5fef228 --- /dev/null +++ b/android/app/src/main/res/values-night-v31/styles.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..dbc9ea9 --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/android/app/src/main/res/values-v31/styles.xml b/android/app/src/main/res/values-v31/styles.xml new file mode 100644 index 0000000..d0a68e9 --- /dev/null +++ b/android/app/src/main/res/values-v31/styles.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..0d1fa8f --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..d2ffbff --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,18 @@ +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 +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..2597170 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e1ca574 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +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 diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..536165d --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,25 @@ +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 + }() + + includeBuild("$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" diff --git a/fonts/OpenSans-Bold.ttf b/fonts/OpenSans-Bold.ttf new file mode 100644 index 0000000..a1398b3 Binary files /dev/null and b/fonts/OpenSans-Bold.ttf differ diff --git a/fonts/OpenSans-ExtraBold.ttf b/fonts/OpenSans-ExtraBold.ttf new file mode 100644 index 0000000..08d7185 Binary files /dev/null and b/fonts/OpenSans-ExtraBold.ttf differ diff --git a/fonts/OpenSans-Medium.ttf b/fonts/OpenSans-Medium.ttf new file mode 100644 index 0000000..ba6db9b Binary files /dev/null and b/fonts/OpenSans-Medium.ttf differ diff --git a/fonts/OpenSans-Regular.ttf b/fonts/OpenSans-Regular.ttf new file mode 100644 index 0000000..1dc226d Binary files /dev/null and b/fonts/OpenSans-Regular.ttf differ diff --git a/fonts/OpenSans-SemiBold.ttf b/fonts/OpenSans-SemiBold.ttf new file mode 100644 index 0000000..66acb20 Binary files /dev/null and b/fonts/OpenSans-SemiBold.ttf differ diff --git a/images/background_login.png b/images/background_login.png new file mode 100644 index 0000000..aaf1a8a Binary files /dev/null and b/images/background_login.png differ diff --git a/images/grafik.png b/images/grafik.png new file mode 100644 index 0000000..23531a5 Binary files /dev/null and b/images/grafik.png differ diff --git a/images/kd_logo.png b/images/kd_logo.png new file mode 100644 index 0000000..9360fcd Binary files /dev/null and b/images/kd_logo.png differ diff --git a/images/kd_splash.png b/images/kd_splash.png new file mode 100644 index 0000000..9475502 Binary files /dev/null and b/images/kd_splash.png differ diff --git a/images/logo_kedungdoro.png b/images/logo_kedungdoro.png new file mode 100644 index 0000000..fb21859 Binary files /dev/null and b/images/logo_kedungdoro.png differ diff --git a/images/logo_pramita.png b/images/logo_pramita.png new file mode 100644 index 0000000..42c0770 Binary files /dev/null and b/images/logo_pramita.png differ diff --git a/images/order_success.png b/images/order_success.png new file mode 100644 index 0000000..6801e4b Binary files /dev/null and b/images/order_success.png differ diff --git a/images/pramita_login.png b/images/pramita_login.png new file mode 100644 index 0000000..7da4602 Binary files /dev/null and b/images/pramita_login.png differ diff --git a/images/pramita_splash.png b/images/pramita_splash.png new file mode 100644 index 0000000..e3cde18 Binary files /dev/null and b/images/pramita_splash.png differ diff --git a/images/registrasi_sukses.png b/images/registrasi_sukses.png new file mode 100644 index 0000000..3aab58a Binary files /dev/null and b/images/registrasi_sukses.png differ diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,34 @@ +**/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 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..7c56964 --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..367a0d9 --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,616 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.mitraCorporate; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.mitraCorporate.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.mitraCorporate.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.mitraCorporate.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.mitraCorporate; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.mitraCorporate; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..8e3ca5d --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..6266644 --- /dev/null +++ b/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Flutter +import UIKit + +@main +@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) + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "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" + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..dc9ada4 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..7353c41 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..797d452 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..6ed2d93 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cd7b00 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..fe73094 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..321773c Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..797d452 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..502f463 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..0ec3034 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..0ec3034 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..e9f5fea Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..84ac32a Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..8953cba Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..0467bf1 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json new file mode 100644 index 0000000..8bb185b --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "background.png", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "darkbackground.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png new file mode 100644 index 0000000..3107d37 Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png new file mode 100644 index 0000000..71e9c81 Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..00cabce --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "LaunchImage.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "LaunchImage@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "LaunchImage@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..71e9c81 Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..71e9c81 Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..71e9c81 Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# 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. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..55c0cae --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 0000000..d3eff3a --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Mitra Corporate + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + mitra_corporate + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + UIStatusBarHidden + + + diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +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. + } + +} diff --git a/lib/app/constant.dart b/lib/app/constant.dart new file mode 100644 index 0000000..87a1a81 --- /dev/null +++ b/lib/app/constant.dart @@ -0,0 +1,270 @@ +import 'package:mitra_corporate/app/route.dart'; +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:mitra_corporate/model/menu_model.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../model/surat_jalan_model.dart'; +import '../widgets/custom_snackbar_widget.dart'; + +class Constant { + static String baseUrl = ""; + static String protocol = ""; + static String tokenName = "oneMitraPramita"; + static double designHeight = 1024; + static double designWidth = 1440; + static String version = "Versi 1.01"; + + static String ipAddress = ""; + + // //color Theme + // static Color textBlack = Color(0xff212B36); + // static Color textGrey = Color(0xff919EAB); + // static Color textBlue = Color(0xff1939B7); + // static Color textWhite = Color(0xffffffff); + // static Color backgroundColor = Color(0xffFCFAFA); + // static Color primaryBlue = Color(0xff1939B7); + // static Color buttonPrimary1 = Color(0xff1E1BD3); + // static Color buttonPrimary2 = Color(0xff5959FF); + + //typoGraphy + static TextStyle h1_700({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 36), + fontWeight: FontWeight.w700, + ); + } + + static TextStyle h2_600({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 28), + fontWeight: FontWeight.w600, + ); + } + + static TextStyle h3_400({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 24), + fontWeight: FontWeight.w600, + ); + } + + static TextStyle h4_600({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 18), + fontWeight: FontWeight.w600, + ); + } + + static TextStyle body1_600({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 16), + fontWeight: FontWeight.w600, + ); + } + + static TextStyle body2_400({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 16), + fontWeight: FontWeight.w500, + ); + } + + static TextStyle body2_700({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 16), + fontWeight: FontWeight.w700, + ); + } + + static TextStyle body3_600({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 14), + fontWeight: FontWeight.w600, + ); + } + + static TextStyle body3_500({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 14), + fontWeight: FontWeight.w500, + ); + } + + static TextStyle body3_400({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 14), + fontWeight: FontWeight.w400, + ); + } + + static TextStyle caption1_600({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 12), + fontWeight: FontWeight.w600, + ); + } + + static TextStyle caption1_400({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 12), + fontWeight: FontWeight.w400, + ); + } + + static TextStyle caption2_600({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 10), + fontWeight: FontWeight.w600, + ); + } + + static TextStyle caption2_400({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 10), + fontWeight: FontWeight.w400, + ); + } + + static TextStyle button_large({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 15), + fontWeight: FontWeight.w700, + ); + } + + static TextStyle button_medium({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 14), + fontWeight: FontWeight.w700, + ); + } + + static TextStyle button_small({required BuildContext context}) { + return TextStyle( + fontSize: Constant.getActualX(context: context, x: 13), + fontWeight: FontWeight.w700, + ); + } + + //size convertion + static double getActualX({ + required BuildContext context, + required double x, + }) { + return x / designWidth * MediaQuery.of(context).size.width; + } + + static double getActualY({ + required BuildContext context, + required double y, + }) { + return y / designHeight * MediaQuery.of(context).size.height; + } + + static String convertToIdr(dynamic number, int decimalDigit) { + NumberFormat currencyFormatter = NumberFormat.currency( + locale: 'id', + symbol: 'Rp ', + decimalDigits: decimalDigit, + ); + return currencyFormatter.format(number); + } + + //color Theme + static Color textBlack = Color(0xff212B36); + static Color textGrey = Color(0xff637381); + static Color textBlue = Color(0xff1939B7); + static Color textWhite = Color(0xffffffff); + static Color textYellow = Color(0xffB78103); + static Color textGreen = Color(0xff229A16); + static Color textPurple = Color(0xffBF07FF); + static Color backgroundColor = Color(0xffFCFAFA); + static Color primaryBlue = Color(0xff1939B7); + static Color buttonPrimary1 = Color(0xff1E1BD3); + static Color buttonPrimary2 = Color(0xff5959FF); + static Color grey_200 = Color(0xffF4F6F8); + static Color yellow_016 = Color(0xffFFC107).withOpacity(0.16); + static Color green_016 = Color(0xff54D62C).withOpacity(0.16); + static Color blue_016 = Color(0xff1890FF).withOpacity(0.16); + static Color green = Color(0xff00AB55); + static Color purple_016 = Color(0xffbf07ff).withOpacity(0.16); + static Color selectedMenuBg = Color(0xffFF0000).withOpacity(0.1); +//Pramita color + static Color primaryRed = Color(0xffFF0000); + static List dateFilter = [ + dateFilterModel(id: "1", name: "Tanggal Surat Jalan"), + dateFilterModel(id: "2", name: "Tanggal Kedatangan"), + ]; + + static List menuList = [ + MenuModel( + icon: EvaIcons.grid, + subValue: [1], + title: "Dashboard", + index: 1, + mainValue: 1), + MenuModel( + icon: EvaIcons.fileAdd, + subValue: [2], + title: "Registrasi Pasien", + index: 2, + mainValue: 2), + MenuModel( + icon: EvaIcons.clipboard, + subValue: [3, 5], + title: "Kirim Order", + index: 3, + mainValue: 3), + MenuModel( + icon: EvaIcons.shoppingCart, + subValue: [6], + title: "List Order", + index: 4, + mainValue: 6), + // MenuModel( + // icon: EvaIcons.fileText, + // subValue: [4, 7], + // title: "Hasil", + // index: 5, + // mainValue: 4), + ]; + static List getMenuModel() { + List data = menuList; + data.sort( + (a, b) => a.index.compareTo(b.index), + ); + return data; + } + + static autoLogout({ + required BuildContext context, + required String msg, + bool? isSplashScreen, + }) async { + if (msg == "Invalid token") { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.remove(tokenName); + Navigator.pushNamed(context, loginRoute); + if (isSplashScreen != true) { + SanckbarWidget( + context, "Invalid token, please login again", snackbarType.warning); + } + } + } + + static setBaseUrl(String text, String? protocol) { + var prt = protocol; + if (protocol == null) { + prt = "https://"; + } + + // baseUrl = "$protocol//devbandungraya.aplikasi.web.id/one-api/one_mitra/"; + // ipAddress = "devbandungraya.aplikasi.web.id"; + // protocol = protocol; + baseUrl = "$protocol//$text/one-api/one_mitra/"; + ipAddress = text; + print(baseUrl); + } +} diff --git a/lib/app/register_webview.dart b/lib/app/register_webview.dart new file mode 100644 index 0000000..ea73c7e --- /dev/null +++ b/lib/app/register_webview.dart @@ -0,0 +1,7 @@ +import 'package:webview_flutter/webview_flutter.dart'; +import 'package:webview_flutter_web/webview_flutter_web.dart'; + +void registerWebViewWebImplementation() { + // WebView.platform = WebWebViewPlatform(); + WebViewPlatform.instance = WebWebViewPlatform(); +} diff --git a/lib/app/register_webview_rtub.dart b/lib/app/register_webview_rtub.dart new file mode 100644 index 0000000..61b180c --- /dev/null +++ b/lib/app/register_webview_rtub.dart @@ -0,0 +1,3 @@ +void registerWebViewWebImplementation() { + // +} diff --git a/lib/app/route.dart b/lib/app/route.dart new file mode 100644 index 0000000..5c16f04 --- /dev/null +++ b/lib/app/route.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:mitra_corporate/screen/home/home_screen.dart'; +import 'package:mitra_corporate/screen/loging/loging.dart'; +import 'package:mitra_corporate/screen/splash_screen.dart/splash_screen.dart'; + +import '../screen/login/login_screen.dart'; + +const loginRoute = "/LoginScreen"; +const registrasiPasienRoute = "/RegistrasiPasienScrenn"; +const suratJalanRoute = "/SuratJalanScreen"; +const suratJalanDetailRoute = "/SuratJalanDetailScreen"; +const homeRoute = "/homeRoute"; + +const dashboardRoute = "/DashboardScreen"; +const splashRoute = "/splashRoute"; +const logingRoute = "/logingRoute"; + +class AppRoute { + static Route? generateRoute(RouteSettings settings) { + if (settings.name == loginRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + padding: const EdgeInsets.all(0), + textScaler: TextScaler.linear(1.0), + ), + child: LoginScreen()); + }); + } + + // if (settings.name == dashboardRoute) { + // return MaterialPageRoute(builder: (context) { + // return MediaQuery( + // data: MediaQuery.of(context).copyWith( + // textScaleFactor: 1.0, + // padding: const EdgeInsets.all(0), + // ), + // child: DashboardScreen()); + // }); + // } + if (settings.name == homeRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + padding: const EdgeInsets.all(0), + textScaler: TextScaler.linear(1.0), + ), + child: HomeScreen()); + }); + } + + // if (settings.name == registrasiPasienRoute) { + // return MaterialPageRoute(builder: (context) { + // return MediaQuery( + // data: MediaQuery.of(context).copyWith( + // textScaleFactor: 1.0, + // padding: const EdgeInsets.all(0), + // ), + // child: const RegistrasiPasienScreen()); + // }); + // } + + // if (settings.name == suratJalanRoute) { + // return MaterialPageRoute(builder: (context) { + // return MediaQuery( + // data: MediaQuery.of(context).copyWith( + // textScaleFactor: 1.0, + // padding: const EdgeInsets.all(0), + // ), + // child: const SuratJalanScreen()); + // }); + // } + + // if (settings.name == suratJalanDetailRoute) { + // return MaterialPageRoute(builder: (context) { + // return MediaQuery( + // data: MediaQuery.of(context).copyWith( + // textScaleFactor: 1.0, + // padding: const EdgeInsets.all(0), + // ), + // child: const SuratJalanDetailScreen()); + // }); + // } + if (settings.name == splashRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + padding: const EdgeInsets.all(0), + textScaler: TextScaler.linear(1.0), + ), + child: const SplashScreen()); + }); + } + if (settings.name == logingRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + padding: const EdgeInsets.all(0), + textScaler: TextScaler.linear(1.0), + ), + child: const LogingScreen()); + }); + } + return null; + } +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..8e9bc26 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,162 @@ +import 'dart:html'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:mitra_corporate/screen/splash_screen.dart/splash_screen.dart'; + +import 'app/constant.dart'; +import 'app/route.dart'; + +import 'app/register_webview_rtub.dart' + if (dart.library.html) 'app/register_webview.dart'; + +void main() { + // runApp(const MyApp()); + // registerWebViewWebImplementation(); + WidgetsFlutterBinding.ensureInitialized(); + Constant.setBaseUrl(window.location.host, window.location.protocol); + runApp(ProviderScope(observers: [], child: MyApp())); + + // FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); + // runApp(ProviderScope(observers: [Logger()], child: MyApp())); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + // localizationsDelegates: [ + // DefaultMaterialLocalizations.delegate, + // DefaultCupertinoLocalizations.delegate, + // DefaultWidgetsLocalizations.delegate, + // ], + // locale: Locale("id", "ID"), + debugShowCheckedModeBanner: false, + title: 'App Mitra', + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + // supportedLocales: [ + // Locale('en', ''), + // Locale('id', ''), // arabic, no country code + // ], + theme: ThemeData( + useMaterial3: true, + colorScheme: ColorScheme.light( + brightness: Brightness.light, + primary: Constant.primaryRed, + surfaceTint: Color(0xff904a46), + onPrimary: Color(0xffffffff), + primaryContainer: Color(0xffffdad7), + onPrimaryContainer: Color(0xff3b0809), + secondary: Color(0xff775654), + onSecondary: Color(0xffffffff), + secondaryContainer: Color(0xffffdad7), + onSecondaryContainer: Color(0xff2c1514), + tertiary: Color(0xff735b2e), + onTertiary: Color(0xffffffff), + tertiaryContainer: Color(0xffffdea7), + onTertiaryContainer: Color(0xff271900), + error: Color(0xffba1a1a), + onError: Color(0xffffffff), + errorContainer: Color(0xffffdad6), + onErrorContainer: Color(0xff410002), + surface: Color(0xfffff8f7), + onSurface: Color(0xff231919), + onSurfaceVariant: Color(0xff534342), + outline: Color(0xff857371), + outlineVariant: Color(0xffd8c2c0), + shadow: Color(0xff000000), + scrim: Color(0xff000000), + inverseSurface: Color(0xff382e2d), + inversePrimary: Color(0xffffb3ae), + primaryFixed: Color(0xffffdad7), + onPrimaryFixed: Color(0xff3b0809), + primaryFixedDim: Color(0xffffb3ae), + onPrimaryFixedVariant: Color(0xff733330), + secondaryFixed: Color(0xffffdad7), + onSecondaryFixed: Color(0xff2c1514), + secondaryFixedDim: Color(0xffe7bdb9), + onSecondaryFixedVariant: Color(0xff5d3f3d), + tertiaryFixed: Color(0xffffdea7), + onTertiaryFixed: Color(0xff271900), + tertiaryFixedDim: Color(0xffe2c28c), + onTertiaryFixedVariant: Color(0xff594319), + surfaceDim: Color(0xffe8d6d4), + surfaceBright: Color(0xfffff8f7), + surfaceContainerLowest: Color(0xffffffff), + surfaceContainerLow: Color(0xfffff0ef), + surfaceContainer: Color(0xfffceae8), + surfaceContainerHigh: Color(0xfff6e4e2), + surfaceContainerHighest: Color(0xfff1dedd), + ), + scaffoldBackgroundColor: Constant.backgroundColor, + dialogBackgroundColor: Colors.white, + buttonTheme: ButtonThemeData( + buttonColor: Constant.primaryRed, + ), + cardColor: Colors.white, + cardTheme: CardTheme(color: Colors.white), + datePickerTheme: DatePickerThemeData( + locale: Locale("id", 'ID'), + backgroundColor: Colors.white, + headerForegroundColor: Colors.white, + headerBackgroundColor: Constant.primaryRed), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ButtonStyle( + backgroundColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.pressed)) { + return Constant.primaryRed.withOpacity(0.5); + } + return Constant.primaryRed; + }), + shape: WidgetStatePropertyAll(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))))), + fontFamily: "OpenSans", + primaryColor: Constant.primaryRed, + primaryColorLight: Constant.primaryRed, + primarySwatch: MaterialColor(0xffFF0000, { + 0: Color(0xffFF0000), + 50: Color(0xffFF0000), + 100: Color(0xffFF0000), + 200: Color(0xffFF0000), + 300: Color(0xffFF0000), + 400: Color(0xffFF0000), + 500: Color(0xffFF0000), + 600: Color(0xffFF0000), + 700: Color(0xffFF0000), + 800: Color(0xffFF0000), + 900: Color(0xffFF0000) + }), + // primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity), + initialRoute: splashRoute, + + onGenerateRoute: AppRoute.generateRoute, + home: SplashScreen(), + ); + } +} + +class Logger extends ProviderObserver { + @override + void didUpdateProvider( + ProviderBase provider, + Object? previousValue, + Object? newValue, + ProviderContainer container, + ) { + // var a = jsonEncode(newValue); + print(''' +{ + "provider": "${provider.name ?? provider.runtimeType}", + "newValue": "$newValue" +}'''); + } +} diff --git a/lib/model/auth_model.dart b/lib/model/auth_model.dart new file mode 100644 index 0000000..b11b0a0 --- /dev/null +++ b/lib/model/auth_model.dart @@ -0,0 +1,52 @@ +class AuthModel { + String? mUserID; + String? mUserMCompanyID; + String? mUserMMouID; + String? mUserUsername; + String? companyName; + String? MUserSRegionalID; + String? ip; + String? agent; + String? token; + bool? isRememberMe; + + AuthModel( + {this.mUserID, + this.mUserMCompanyID, + this.mUserMMouID, + this.companyName, + this.ip, + this.agent, + this.token, + this.mUserUsername, + this.MUserSRegionalID, + this.isRememberMe}); + + AuthModel.fromJson(Map json) { + mUserID = json['M_UserID']; + mUserMCompanyID = json['M_UserM_CompanyID']; + mUserMMouID = json['M_UserM_MouID']; + companyName = json['company_name']; + mUserUsername = json['M_UserUsername']; + ip = json['ip']; + agent = json['agent']; + token = json['token']; + MUserSRegionalID = json['M_UserS_RegionalID']; + isRememberMe = json['is_remember_me']; + } + + Map toJson() { + final Map data = {}; + data['M_UserID'] = mUserID; + data['M_UserM_CompanyID'] = mUserMCompanyID; + data['M_UserM_MouID'] = mUserMMouID; + data['company_name'] = companyName; + data['ip'] = ip; + data['agent'] = agent; + data['M_UserUsername'] = mUserUsername; + data['token'] = token; + data['M_UserS_RegionalID'] = MUserSRegionalID; + data['is_remember_me'] = isRememberMe; + return data; + } +} diff --git a/lib/model/chart_data_model.dart b/lib/model/chart_data_model.dart new file mode 100644 index 0000000..6fac22c --- /dev/null +++ b/lib/model/chart_data_model.dart @@ -0,0 +1,56 @@ +class ChartDataModel { + List? n; + List? y; + List? t; + List? s; + + ChartDataModel({this.n, this.y, this.t, this.s}); + + ChartDataModel.fromJson(Map json) { + if (json['N'] != null) { + n = []; + json['N'].forEach((v) { + if (v.isNotEmpty) { + n!.add(v.toString()); + } + }); + } + if (json['Y'] != null) { + y = []; + json['Y'].forEach((v) { + if (v.isNotEmpty) { + y!.add(v.toString()); + } + }); + } + if (json['T'] != null) { + t = []; + json['T'].forEach((v) { + if (v.isNotEmpty) { + t!.add(v.toString()); + } + }); + } + if (json['S'] != null) { + s = []; + json['S'].forEach((v) { + if (v.isNotEmpty) { + s!.add(v.toString()); + } + }); + } + // n = json['N'].cast(); + // p = json['P'].cast(); + // d = json['D'].cast(); + // s = json['S'].cast(); + } + + Map toJson() { + final Map data = {}; + data['N'] = n; + data['Y'] = y; + data['T'] = t; + data['S'] = s; + return data; + } +} diff --git a/lib/model/create_surat_jalan_model.dart b/lib/model/create_surat_jalan_model.dart new file mode 100644 index 0000000..cd497a5 --- /dev/null +++ b/lib/model/create_surat_jalan_model.dart @@ -0,0 +1,101 @@ +import 'package:mitra_corporate/model/order_model.dart'; + +class CreateSuratJalanModel { + Order? order; + List? orderDetail; + String? token; + + CreateSuratJalanModel({this.order, this.orderDetail, this.token}); + + CreateSuratJalanModel.fromJson(Map json) { + order = json['order'] != null ? Order.fromJson(json['order']) : null; + if (json['order_detail'] != null) { + orderDetail = []; + json['order_detail'].forEach((v) { + orderDetail!.add(OrderModel.fromJson(v)); + }); + } + token = json['token']; + } + + Map toJson() { + final Map data = {}; + if (order != null) { + data['order'] = order!.toJson(); + } + if (orderDetail != null) { + data['order_detail'] = orderDetail!.map((v) => v.toJson()).toList(); + } + data['token'] = token; + return data; + } +} + +class Order { + String? staffId; + String? noRef; + String? date; + String? regionalId; + String? regionalName; + String? destinationId; + String? destinationName; + String? temperature; + String? typeId; + String? typeName; + String? noResi; + String? note; + String? branchCode; + String? companyId; + + Order( + {this.staffId, + this.noRef, + this.date, + this.destinationId, + this.temperature, + this.typeId, + this.noResi, + this.typeName, + this.note, + this.regionalId, + this.regionalName, + this.destinationName, + this.branchCode, + this.companyId}); + + Order.fromJson(Map json) { + staffId = json['staff_id']; + noRef = json['no_ref']; + date = json['date']; + destinationId = json['destination_id']; + regionalId = json['regional_id']; + regionalName = json['regional_name']; + destinationName = json['destination_name']; + temperature = json['temperature']; + typeId = json['type_id']; + noResi = json['no_resi']; + note = json['note']; + branchCode = json['branch_code']; + companyId = json['company_id']; + typeName = json['type_name']; + } + + Map toJson() { + final Map data = {}; + data['staff_id'] = staffId; + data['no_ref'] = noRef; + data['date'] = date; + data['destination_id'] = destinationId; + data['regional_id'] = regionalId; + data['regional_name'] = regionalName; + data['destination_name'] = destinationName; + data['temperature'] = temperature; + data['type_id'] = typeId; + data['no_resi'] = noResi; + data['note'] = note; + data['branch_code'] = branchCode; + data['company_id'] = companyId; + data['type_name'] = typeName; + return data; + } +} diff --git a/lib/model/delivery_type_model.dart b/lib/model/delivery_type_model.dart new file mode 100644 index 0000000..f8399eb --- /dev/null +++ b/lib/model/delivery_type_model.dart @@ -0,0 +1,21 @@ +class DeliveryTypeModel { + String? id; + String? name; + String? isAgent; + + DeliveryTypeModel({this.id, this.name, this.isAgent}); + + DeliveryTypeModel.fromJson(Map json) { + id = json['id']; + name = json['name']; + isAgent = json['isAgent']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['name'] = name; + data['isAgent'] = isAgent; + return data; + } +} diff --git a/lib/model/destination_model.dart b/lib/model/destination_model.dart new file mode 100644 index 0000000..62eaf3b --- /dev/null +++ b/lib/model/destination_model.dart @@ -0,0 +1,25 @@ +class DestinationModel { + String? branchId; + String? branchCode; + String? branchName; + String? regionalId; + + DestinationModel( + {this.branchId, this.branchCode, this.branchName, this.regionalId}); + + DestinationModel.fromJson(Map json) { + branchId = json['branch_id']; + branchCode = json['branch_code']; + branchName = json['branch_name']; + regionalId = json['regional_id']; + } + + Map toJson() { + final Map data = {}; + data['branch_id'] = branchId; + data['branch_code'] = branchCode; + data['branch_name'] = branchName; + data['regional_id'] = regionalId; + return data; + } +} diff --git a/lib/model/menu_model.dart b/lib/model/menu_model.dart new file mode 100644 index 0000000..9000527 --- /dev/null +++ b/lib/model/menu_model.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class MenuModel { + String title = ""; + int mainValue = 0; + List subValue = []; + IconData icon = Icons.abc; + int index = 0; + MenuModel( + {required this.icon, + required this.subValue, + required this.title, + required this.index, + required this.mainValue}); +} diff --git a/lib/model/order_model.dart b/lib/model/order_model.dart new file mode 100644 index 0000000..81530cd --- /dev/null +++ b/lib/model/order_model.dart @@ -0,0 +1,190 @@ +class OrderModel { + String? orderId; + String? orderNumber; + String? patientId; + String? patientName; + String? status; + String? prefix; + String? suffix; + String? dob; + String? NIK; + String? NIP; + String? title; + String? sexID; + String? hp; + String? address; + String? note; + String? diagnosis; + String? statusQr; + String? statusPemeriksaan; + String? jabatan; + String? kedudukan; + String? lokasi; + String? pekerjaan; + String? noRM; + String? isQr; + List? sample; + List? testsID; + List? testDetail; + List? bahan; + List? tests; + List? paket; + List? paketDetail; + + OrderModel( + {this.orderId, + this.orderNumber, + this.patientId, + this.patientName, + this.sample, + this.tests, + this.testsID, + this.status, + this.NIK, + this.NIP, + this.address, + this.diagnosis, + this.dob, + this.hp, + this.note, + this.prefix, + this.noRM, + this.sexID, + this.suffix, + this.title, + this.testDetail, + this.paket, + this.paketDetail, + this.statusQr, + this.statusPemeriksaan, + this.jabatan, + this.kedudukan, + this.lokasi, + this.pekerjaan, + this.isQr, + this.bahan}); + + OrderModel.fromJson(Map json) { + orderId = json['order_id']; + orderNumber = json['order_number']; + patientId = json['patient_id']; + patientName = json['patient_name']; + status = json['status']; + prefix = json['prefix']; + suffix = json['suffix']; + dob = json['dob']; + noRM = json['noRM']; + NIK = json['NIK']; + NIP = json['NIP']; + title = json['title']; + sexID = json['sexID']; + hp = json['hp']; + address = json['address']; + note = json['note']; + diagnosis = json['diagnosis']; + statusQr = json['status_qr']; + statusPemeriksaan = json['status_pemeriksaan']; + jabatan = json['jabatan']; + kedudukan = json['kedudukan']; + lokasi = json['lokasi']; + pekerjaan = json['pekerjaan']; + isQr = json['is_qr']; + // sample = json['sample'].cast(); + // bahan = json['bahan'].cast(); + // tests = json['tests'].cast(); + // testsID = json['testsID'].cast(); + if (json['sample'] != null) { + sample = []; + + json['sample'].forEach((v) { + if (v.isNotEmpty) { + sample!.add(v.toString()); + } + }); + } + if (json['bahan'] != null) { + bahan = []; + json['bahan'].forEach((v) { + if (v.isNotEmpty) { + bahan!.add(v.toString()); + } + }); + } + if (json['tests'] != null) { + tests = []; + json['tests'].forEach((v) { + if (v != "") { + tests!.add(v.toString()); + } + }); + } + if (json['testsID'] != null) { + testsID = []; + json['testsID'].forEach((v) { + if (v.isNotEmpty) { + testsID!.add(v.toString()); + } + }); + } + if (json['testDetail'] != null) { + testDetail = []; + json['testDetail'].forEach((v) { + if (v.isNotEmpty) { + testDetail!.add(v.toString()); + } + }); + } + if (json['packet'] != null) { + paket = []; + json['packet'].forEach((v) { + if (v != "") { + paket!.add(v.toString()); + } + }); + } + if (json['packetDetail'] != null) { + paketDetail = []; + json['packetDetail'].forEach((v) { + if (v.isNotEmpty) { + paketDetail!.add(v.toString()); + } + }); + } + } + + Map toJson() { + final Map data = {}; + data['order_id'] = orderId; + data['order_number'] = orderNumber; + data['patient_id'] = patientId; + data['patient_name'] = patientName; + data['sample'] = sample; + data['bahan'] = bahan; + data['tests'] = tests; + data['status'] = status; + data['testsID'] = testsID; + data['NIK'] = NIK; + data['nip'] = NIP; + data['address'] = address; + data['diagnosis'] = diagnosis; + data['dob'] = dob; + data['hp'] = hp; + data['note'] = note; + data['prefix'] = prefix; + data['sexID'] = sexID; + data['suffix'] = suffix; + data['title'] = title; + data['testDetail'] = testDetail; + data['packet'] = paket; + data['packetDetail'] = paketDetail; + data['status_qr'] = statusQr; + data['status_pemeriksaan'] = statusPemeriksaan; + data['kedudukan'] = kedudukan; + data['jabatan'] = jabatan; + data['lokasi'] = lokasi; + data['pekerjaan'] = pekerjaan; + data['noRM'] = noRM; + data['is_qr'] = isQr; + return data; + } +} diff --git a/lib/model/patient_Model.dart b/lib/model/patient_Model.dart new file mode 100644 index 0000000..7a210e7 --- /dev/null +++ b/lib/model/patient_Model.dart @@ -0,0 +1,80 @@ +class PatientModel { + String? id; + String? prefix; + String? name; + String? suffix; + String? dob; + String? titleId; + String? sexId; + String? hp; + String? nik; + String? nip; + String? noRM; + String? jabatan; + String? kedudukan; + String? lokasi; + String? pekerjaan; + String? address; + String? statusDelete; + + PatientModel( + {this.id, + this.prefix, + this.name, + this.suffix, + this.dob, + this.titleId, + this.sexId, + this.hp, + this.nik, + this.nip, + this.noRM, + this.jabatan, + this.kedudukan, + this.lokasi, + this.pekerjaan, + this.statusDelete, + this.address}); + + PatientModel.fromJson(Map json) { + id = json['id']; + prefix = json['prefix']; + name = json['name']; + suffix = json['suffix']; + dob = json['dob']; + titleId = json['title_id']; + sexId = json['sex_id']; + hp = json['hp']; + nik = json['nik']; + nip = json['nip']; + noRM = json['noRM']; + address = json['address']; + jabatan = json['jabatan']; + kedudukan = json['kedudukan']; + lokasi = json['lokasi']; + pekerjaan = json['pekerjaan']; + statusDelete = json['status_delete']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['prefix'] = prefix; + data['name'] = name; + data['suffix'] = suffix; + data['dob'] = dob; + data['title_id'] = titleId; + data['sex_id'] = sexId; + data['hp'] = hp; + data['address'] = address; + data['nik'] = nik; + data['nip'] = nip; + data['noRM'] = noRM; + data['kedudukan'] = kedudukan; + data['jabatan'] = jabatan; + data['lokasi'] = lokasi; + data['pekerjaan'] = pekerjaan; + data['status_delete'] = statusDelete; + return data; + } +} diff --git a/lib/model/registration_filter_model.dart b/lib/model/registration_filter_model.dart new file mode 100644 index 0000000..8d8b98d --- /dev/null +++ b/lib/model/registration_filter_model.dart @@ -0,0 +1,74 @@ +import 'package:mitra_corporate/model/destination_model.dart'; +import 'package:mitra_corporate/widgets/custom_text_field.dart'; + +class RegistrationFilterModel { + List? titles; + List? gender; + List? regionals; + + RegistrationFilterModel({this.titles, this.gender, this.regionals}); + + RegistrationFilterModel.fromJson(Map json) { + if (json['titles'] != null) { + titles = []; + json['titles'].forEach((v) { + titles!.add(CustomDropDownModel.fromJson(v)); + }); + } + if (json['gender'] != null) { + gender = []; + json['gender'].forEach((v) { + gender!.add(CustomDropDownModel.fromJson(v)); + }); + } + if (json['regional'] != null) { + regionals = []; + json['regional'].forEach((v) { + regionals!.add(RegionalModel.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = {}; + if (titles != null) { + data['titles'] = titles!.map((v) => v.toJson()).toList(); + } + if (gender != null) { + data['gender'] = gender!.map((v) => v.toJson()).toList(); + } + if (regionals != null) { + data['regional'] = regionals!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class RegionalModel { + String? regionalId; + String? regionalName; + List? branch; + + RegionalModel({this.regionalId, this.regionalName, this.branch}); + + RegionalModel.fromJson(Map json) { + regionalId = json['regional_id']; + regionalName = json['regional_name']; + if (json['branch'] != null) { + branch = []; + json['branch'].forEach((v) { + branch!.add(DestinationModel.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = {}; + data['regional_id'] = regionalId; + data['regional_name'] = regionalName; + if (branch != null) { + data['branch'] = branch!.map((v) => v.toJson()).toList(); + } + return data; + } +} diff --git a/lib/model/registration_model.dart b/lib/model/registration_model.dart new file mode 100644 index 0000000..e5fbf1f --- /dev/null +++ b/lib/model/registration_model.dart @@ -0,0 +1,378 @@ +import 'package:flutter/material.dart'; + +class RegistrationModel { + String? patientId; + String? orderID; + List? paket; + PatientData? patientData; + List? tests; + List? specimens; + List? bahan; + String? total; + String? token; + + RegistrationModel( + {this.patientData, + this.tests, + this.specimens, + this.total, + this.bahan, + this.patientId, + this.paket, + this.orderID, + this.token}); + + RegistrationModel.fromJson(Map json) { + patientData = json['patient_data'] != null + ? PatientData.fromJson(json['patient_data']) + : null; + if (json['tests'] != null) { + tests = []; + json['tests'].forEach((v) { + tests!.add(Tests.fromJson(v)); + }); + } + if (json['specimens'] != null) { + specimens = []; + json['specimens'].forEach((v) { + specimens!.add(Specimens.fromJson(v)); + }); + } + if (json['bahan'] != null) { + bahan = []; + json['bahan'].forEach((v) { + bahan!.add(Bahan.fromJson(v)); + }); + } + if (json['paket'] != null) { + paket = []; + json['paket'].forEach((v) { + paket!.add(Paket.fromJson(v)); + }); + } + total = json['total']; + + patientId = json['patient_id']; + token = json['token']; + orderID = json['orderID']; + } + + Map toJson() { + final Map data = {}; + if (patientData != null) { + data['patient_data'] = patientData!.toJson(); + } + if (tests != null) { + data['tests'] = tests!.map((v) => v.toJson()).toList(); + } + if (specimens != null) { + data['specimens'] = specimens!.map((v) => v.toJson()).toList(); + } + if (bahan != null) { + data['bahan'] = bahan!.map((v) => v.toJson()).toList(); + } + if (paket != null) { + data['paket'] = paket!.map((v) => v.toJson()).toList(); + } + data['total'] = total; + data['patient_id'] = patientId; + data['token'] = token; + + data['orderID'] = orderID; + return data; + } +} + +class PatientData { + String? saluation; + String? prefix; + String? name; + String? suffix; + String? dob; + String? nik; + String? nip; + String? withoutNIK; + String? gender; + String? hp; + String? address; + String? diagnosis; + String? note; + String? noRM; + String? jabatan; + String? kedudukan; + String? lokasi; + String? pekerjaan; + + PatientData( + {this.saluation, + this.prefix, + this.name, + this.suffix, + this.dob, + this.nik, + this.nip, + this.withoutNIK, + this.gender, + this.hp, + this.address, + this.diagnosis, + this.noRM, + this.jabatan, + this.kedudukan, + this.lokasi, + this.pekerjaan, + this.note}); + + PatientData.fromJson(Map json) { + saluation = json['saluation']; + prefix = json['prefix']; + name = json['name']; + suffix = json['suffix']; + dob = json['dob']; + nik = json['nik']; + nip = json['nip']; + withoutNIK = json['without_nik']; + gender = json['gender']; + hp = json['hp']; + address = json['address']; + diagnosis = json['diagnosis']; + note = json['note']; + noRM = json['noRM']; + jabatan = json['jabatan']; + kedudukan = json['kedudukan']; + lokasi = json['lokasi']; + pekerjaan = json['pekerjaan']; + } + + Map toJson() { + final Map data = {}; + data['saluation'] = saluation; + data['prefix'] = prefix; + data['name'] = name; + data['suffix'] = suffix; + data['dob'] = dob; + data['nik'] = nik; + data['nip'] = nip; + data['without_nik'] = withoutNIK; + data['gender'] = gender; + data['hp'] = hp; + data['address'] = address; + data['diagnosis'] = diagnosis; + data['note'] = note; + data['noRM'] = noRM; + data['kedudukan'] = kedudukan; + data['jabatan'] = jabatan; + data['lokasi'] = lokasi; + data['pekerjaan'] = pekerjaan; + return data; + } +} + +class Tests { + String? tab; + String? id; + String? detailID; + String? name; + String? isPaket; + List? specimen; + List? bahan; + String? price; + String? date; + String? sasCode; + bool? dateVal; + + Tests( + {this.id, + this.name, + this.specimen, + this.price, + this.date, + this.detailID, + this.tab, + this.isPaket, + this.dateVal, + this.sasCode}); + + Tests.fromJson(Map json) { + id = json['id'].toString(); + tab = json['tab'].toString(); + name = json['name'].toString(); + isPaket = json['is_paket'].toString(); + if (json['specimen'] != null) { + specimen = []; + json['specimen'].forEach((v) { + specimen!.add(Specimens.fromJson(v)); + }); + } + if (json['bahan'] != null) { + bahan = []; + json['bahan'].forEach((v) { + bahan!.add(Bahan.fromJson(v)); + }); + } + price = json['price'].toString(); + detailID = json['detailID'].toString(); + date = json['date'].toString(); + dateVal = json['dateVal']; + sasCode = json['sasCode'].toString(); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['tab'] = tab; + data['name'] = name; + data['is_paket'] = isPaket; + if (specimen != null) { + data['specimen'] = specimen!.map((v) => v.toJson()).toList(); + } + if (bahan != null) { + data['bahan'] = bahan!.map((v) => v.toJson()).toList(); + } + data['price'] = price; + data['detailID'] = detailID; + data['date'] = date; + data['dateVal'] = dateVal; + data['sasCode'] = sasCode; + return data; + } +} + +class Specimens { + String? id; + String? detailID; + String? name; + String? amount; + TextEditingController ctr = TextEditingController(text: "0"); + + Specimens( + {this.id, this.name, this.amount, this.detailID, required this.ctr}); + + Specimens.fromJson(Map json) { + id = json['id']; + detailID = json['detailID']; + name = json['name']; + amount = json['amount']; + ctr = TextEditingController(text: "0"); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['name'] = name; + data['detailID'] = detailID; + data['amount'] = ctr.text; + data["ctr"] = ctr.text; + return data; + } +} + +class Bahan { + String? id; + String? detailID; + String? name; + String? amount; + TextEditingController ctr = TextEditingController(text: "0"); + + Bahan({this.id, this.name, this.amount, this.detailID, required this.ctr}); + + Bahan.fromJson(Map json) { + id = json['id']; + name = json['name']; + amount = json['amount']; + detailID = json['detailID']; + ctr = TextEditingController(text: "0"); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['name'] = name; + data['detailID'] = detailID; + data['amount'] = ctr.text; + data["ctr"] = ctr.text; + return data; + } +} + +class GetSpecimenModel { + String? id; + String? tab; + List? specimen; + List? bahan; + + GetSpecimenModel({this.id, this.tab, this.specimen, this.bahan}); + + GetSpecimenModel.fromJson(Map json) { + id = json['id']; + tab = json['tab']; + if (json['specimen'] != null) { + specimen = []; + json['specimen'].forEach((v) { + specimen!.add(Specimens.fromJson(v)); + }); + } + if (json['bahan'] != null) { + bahan = []; + json['bahan'].forEach((v) { + bahan!.add(Bahan.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['tab'] = tab; + if (specimen != null) { + data['specimen'] = specimen!.map((v) => v.toJson()).toList(); + } + if (bahan != null) { + data['bahan'] = bahan!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class Paket { + String? id; + String? detailID; + String? detail; + String? name; + String? price; + String? type; + String? arrTest; + + Paket({ + this.id, + this.name, + this.detailID, + this.detail, + this.arrTest, + this.price, + this.type, + }); + + Paket.fromJson(Map json) { + id = json['id']; + name = json['name']; + detailID = json['detail_id']; + arrTest = json['arrTest']; + detail = json['detail']; + arrTest = json['price']; + detail = json['type']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['name'] = name; + data['detail'] = detail; + data['arrTest'] = arrTest; + data['detail_id'] = detailID; + data['price'] = price; + data['type'] = type; + + return data; + } +} diff --git a/lib/model/search_order_model.dart b/lib/model/search_order_model.dart new file mode 100644 index 0000000..a5eb56e --- /dev/null +++ b/lib/model/search_order_model.dart @@ -0,0 +1,30 @@ +import 'package:mitra_corporate/model/order_model.dart'; + +class SearchOrderModel { + List? orders; + String? total; + int? totalPage; + + SearchOrderModel({this.orders, this.total, this.totalPage}); + + SearchOrderModel.fromJson(Map json) { + if (json['data'] != null) { + orders = []; + json['data'].forEach((v) { + orders!.add(OrderModel.fromJson(v)); + }); + } + total = json['total']; + totalPage = json['total_page']; + } + + Map toJson() { + final Map data = {}; + if (orders != null) { + data['data'] = orders!.map((v) => v.toJson()).toList(); + } + data['total'] = total; + data['total_page'] = totalPage; + return data; + } +} diff --git a/lib/model/search_patient_model.dart b/lib/model/search_patient_model.dart new file mode 100644 index 0000000..67cc97d --- /dev/null +++ b/lib/model/search_patient_model.dart @@ -0,0 +1,30 @@ +import 'package:mitra_corporate/model/patient_Model.dart'; + +class SearchPatientModel { + List? patients; + String? total; + int? totalPage; + + SearchPatientModel({this.patients, this.total, this.totalPage}); + + SearchPatientModel.fromJson(Map json) { + if (json['data'] != null) { + patients = []; + json['data'].forEach((v) { + patients!.add(PatientModel.fromJson(v)); + }); + } + total = json['total']; + totalPage = json['total_page']; + } + + Map toJson() { + final Map data = {}; + if (patients != null) { + data['data'] = patients!.map((v) => v.toJson()).toList(); + } + data['total'] = total; + data['total_page'] = totalPage; + return data; + } +} diff --git a/lib/model/surat_jalan_detail_model.dart b/lib/model/surat_jalan_detail_model.dart new file mode 100644 index 0000000..6768c94 --- /dev/null +++ b/lib/model/surat_jalan_detail_model.dart @@ -0,0 +1,52 @@ +class SuratJalanDetailModel { + String? deliveryId; + String? deliveryNumber; + String? orderNumber; + String? deliveryDetailId; + String? orderId; + String? date; + String? patientName; + String? status; + String? acceptedSample; + String? rejectedSample; + + SuratJalanDetailModel( + {this.deliveryId, + this.deliveryNumber, + this.deliveryDetailId, + this.orderId, + this.date, + this.orderNumber, + this.patientName, + this.acceptedSample, + this.rejectedSample, + this.status}); + + SuratJalanDetailModel.fromJson(Map json) { + deliveryId = json['delivery_id']; + deliveryNumber = json['delivery_number']; + deliveryDetailId = json['delivery_detail_id']; + orderId = json['order_id']; + date = json['date']; + orderNumber = json['order_number']; + patientName = json['patient_name']; + acceptedSample = json['accepted_sample']; + rejectedSample = json['rejected_sample']; + status = json['status']; + } + + Map toJson() { + final Map data = {}; + data['delivery_id'] = deliveryId; + data['delivery_number'] = deliveryNumber; + data['delivery_detail_id'] = deliveryDetailId; + data['order_id'] = orderId; + data['date'] = date; + data['order_number'] = orderNumber; + data['patient_name'] = patientName; + data['accepted_sample'] = acceptedSample; + data['rejected_sample'] = rejectedSample; + data['status'] = status; + return data; + } +} diff --git a/lib/model/surat_jalan_model.dart b/lib/model/surat_jalan_model.dart new file mode 100644 index 0000000..0a63e24 --- /dev/null +++ b/lib/model/surat_jalan_model.dart @@ -0,0 +1,83 @@ +class SuratJalanModel { + String? id; + String? date; + String? orderNumber; + String? pic; + String? type; + String? destination; + String? status; + String? dateSj; + + SuratJalanModel( + {this.id, + this.date, + this.orderNumber, + this.pic, + this.type, + this.status, + this.dateSj, + this.destination}); + + SuratJalanModel.fromJson(Map json) { + id = json['id']; + date = json['date']; + orderNumber = json['order_number']; + pic = json['pic']; + type = json['type']; + status = json['status']; + destination = json['destination']; + dateSj = json['date_sj']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['date'] = date; + data['order_number'] = orderNumber; + data['pic'] = pic; + data['type'] = type; + data['status'] = status; + data['destination'] = destination; + data['date_sj'] = dateSj; + return data; + } +} + +class SearchSuratJalanModel { + List? suratJalan; + String? total; + int? totalPage; + + SearchSuratJalanModel({this.suratJalan, this.total, this.totalPage}); + + SearchSuratJalanModel.fromJson(Map json) { + if (json['data'] != null) { + suratJalan = []; + json['data'].forEach((v) { + suratJalan!.add(SuratJalanModel.fromJson(v)); + }); + } + total = json['total']; + totalPage = json['total_page']; + } + + Map toJson() { + final Map data = {}; + if (suratJalan != null) { + data['data'] = suratJalan!.map((v) => v.toJson()).toList(); + } + data['total'] = total; + data['total_page'] = totalPage; + return data; + } +} + +class dateFilterModel { + String id; + String name; + + dateFilterModel({ + required this.id, + required this.name, + }); +} diff --git a/lib/model/test_model.dart b/lib/model/test_model.dart new file mode 100644 index 0000000..6e0a603 --- /dev/null +++ b/lib/model/test_model.dart @@ -0,0 +1,76 @@ +class TestModel { + String? tab; + int? tabId; + String? isPaket; + List? items; + + TestModel({this.tab, this.tabId, this.items, this.isPaket}); + + TestModel.fromJson(Map json) { + tab = json['tab']; + tabId = json['tab_id']; + isPaket = json['is_paket']; + if (json['items'] != null) { + items = []; + json['items'].forEach((v) { + items!.add(Items.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = {}; + data['tab'] = tab; + data['tab_id'] = tabId; + data['is_paket'] = isPaket; + if (items != null) { + data['items'] = items!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class Items { + String? testID; + String? testName; + String? testPrice; + String? sasCode; + String? isPaket; + String? arrTest; + String? type; + bool value = false; + + Items( + {this.testID, + this.testName, + this.testPrice, + this.isPaket, + this.arrTest, + this.type, + this.value = false, + this.sasCode}); + + Items.fromJson(Map json) { + testID = json['testID']; + testName = json['testName']; + testPrice = json['testPrice'].toString(); + sasCode = json['sasCode']; + isPaket = json['is_paket']; + arrTest = json['arrTest']; + type = json['type']; + value = false; + } + + Map toJson() { + final Map data = {}; + data['testID'] = testID; + data['testName'] = testName; + data['testPrice'] = testPrice; + data['sasCode'] = sasCode; + data['is_paket'] = isPaket; + data['arrTest'] = arrTest; + data['type'] = type; + data['value'] = value; + return data; + } +} diff --git a/lib/provider/auth_provider.dart b/lib/provider/auth_provider.dart new file mode 100644 index 0000000..c99e4b0 --- /dev/null +++ b/lib/provider/auth_provider.dart @@ -0,0 +1,6 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../model/auth_model.dart'; + +//data auth +final authProvider = StateProvider((ref) => null); diff --git a/lib/provider/dio_provider.dart b/lib/provider/dio_provider.dart new file mode 100644 index 0000000..f03191a --- /dev/null +++ b/lib/provider/dio_provider.dart @@ -0,0 +1,4 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final dioProvider = Provider((ref) => Dio()); diff --git a/lib/provider/menu_provider.dart b/lib/provider/menu_provider.dart new file mode 100644 index 0000000..da134de --- /dev/null +++ b/lib/provider/menu_provider.dart @@ -0,0 +1,5 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final currentMenuProvider = StateProvider((ref) => 1); +final sideBarExpandProvider = StateProvider((ref) => true); +final keywordProvider = StateProvider((ref) => ""); diff --git a/lib/provider/order_provider.dart b/lib/provider/order_provider.dart new file mode 100644 index 0000000..aa3d352 --- /dev/null +++ b/lib/provider/order_provider.dart @@ -0,0 +1,26 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../model/registration_model.dart'; +import '../model/test_model.dart'; + +final EditStepProvider = StateProvider((ref) => 0); +final editOrderLoadingProvider = StateProvider((ref) => false); + +// mendapatkan perubahan value idTipePeriksa +final tabPeriksaprovider = StateProvider((ref) => 1); +// selected test provider berisi list string "idtab|idtes" +final selectedTestEditProvider = + StateProvider>((ref) => List.empty()); +final selectedtestData = StateProvider>((ref) => List.empty()); +//menyimpan data temporary saat registrasi patien selama dialog masih terbuka +final EditDataProvider = + StateProvider((ref) => RegistrationModel()); +//temporary test list +final tempTestListEditProvider = StateProvider>( + (ref) => List.empty(), +); +final selectedPacketEditProvider = + StateProvider>((ref) => List.empty(growable: true)); +//Paket yang sudah terpilih di db untuk nantinya perbandingan apakah paket baru ataukah paket dihapus +final currentPacketProvider = + StateProvider>((ref) => List.empty(growable: true)); diff --git a/lib/provider/registrasi_provider.dart b/lib/provider/registrasi_provider.dart new file mode 100644 index 0000000..834f49f --- /dev/null +++ b/lib/provider/registrasi_provider.dart @@ -0,0 +1,28 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/registration_model.dart'; +import 'package:mitra_corporate/model/test_model.dart'; + +// menerima perubahan value dari onTap simpan perubahan +final registrasiProvider = StateProvider((ref) => 0); +final addOrderLoadingProvider = StateProvider((ref) => false); + +// mendapatkan perubahan value idTipePeriksa +final idTipePeriksaStateProvider = StateProvider((ref) => 1); +// selected test provider berisi list string "idtab|idtes" +final selectedTestProvider = StateProvider>((ref) => List.empty()); +//menyimpan data temporary saat registrasi patien selama dialog masih terbuka +final registrationDataProvider = + StateProvider((ref) => RegistrationModel()); +//temporary test list +final tempTestListProvider = StateProvider>( + (ref) => List.empty(), +); + +final orderNumberProvider = StateProvider((ref) => ""); +final dialogOrderActionProvider = StateProvider((ref) => "new"); +final selectedPacketProvider = + StateProvider>((ref) => List.empty(growable: true)); + +//edit pasien +final registrationEditPatientProvider = + StateProvider((ref) => RegistrationModel()); diff --git a/lib/provider/surat_jalan_provider.dart b/lib/provider/surat_jalan_provider.dart new file mode 100644 index 0000000..d025844 --- /dev/null +++ b/lib/provider/surat_jalan_provider.dart @@ -0,0 +1,22 @@ +import 'package:mitra_corporate/model/surat_jalan_model.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/create_surat_jalan_model.dart'; + +import '../app/constant.dart'; + +// menerima perubahan value dari onTap simpan perubahan +final suratJalanProvider = StateProvider((ref) => 1); + +final createSuratJalanProvider = + StateProvider((ref) => CreateSuratJalanModel()); + +final suratJalankeywordProvider = StateProvider((ref) => ""); +//param untuk detail surat jalan +final suratJalanID = StateProvider((ref) => ""); +final suratJalanNumber = StateProvider((ref) => ""); + +final sjStartDateProvider = StateProvider((ref) => DateTime.now()); +final sjEndDateProvider = StateProvider((ref) => DateTime.now()); + +final sjDateType = + StateProvider((ref) => Constant.dateFilter.first); diff --git a/lib/repository/auth_repository.dart b/lib/repository/auth_repository.dart new file mode 100644 index 0000000..b38957b --- /dev/null +++ b/lib/repository/auth_repository.dart @@ -0,0 +1,75 @@ +import '../app/constant.dart'; +import '../model/auth_model.dart'; +import 'base_repository.dart'; + +class AuthRepository extends BaseRepository { + AuthRepository({required super.dio}); + + Future login({ + required String username, + required String password, + // required String company_id, + // required String regional_id, + }) async { + final param = { + "username": username, + "password": password, + // "company_id": company_id, + // "regional_id": regional_id + + // "username": "alhadad1", + // "doctor_id": "2891", + // "password": "3" + }; + final service = "${Constant.baseUrl}auth/login"; + final resp = await post(param: param, service: service); + final result = AuthModel.fromJson(resp['data']['user']); + result.token = resp['data']['token']; + return result; + } + + Future logout({ + required String M_UserID, + required String M_UserUsername, + }) async { + final param = { + "M_UserID": M_UserID, + "username": M_UserUsername + // "username": "alhadad", + // "doctor_id": "3101210841", + // "password": "riau123" + }; + + final service = "${Constant.baseUrl}auth/logout"; + final resp = await post(param: param, service: service); + + if (resp["status"] == "OK") { + return resp['status']; + } else { + return resp['message']; + } + } + + Future changePassword({ + required String token, + required String current_password, + required String new_password, + required String password_confirmation, + }) async { + final param = { + "token": token, + "current_password": current_password, + "new_password": new_password, + "password_confirmation": password_confirmation + }; + + final service = "${Constant.baseUrl}auth/changepassword"; + final resp = await post(param: param, service: service); + + if (resp["status"] == "OK") { + return resp['data']; + } else { + return resp['data']; + } + } +} diff --git a/lib/repository/base_repository.dart b/lib/repository/base_repository.dart new file mode 100644 index 0000000..5715cca --- /dev/null +++ b/lib/repository/base_repository.dart @@ -0,0 +1,100 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:dio/dio.dart'; + +abstract class BaseRepository { + final Dio dio; + BaseRepository({required this.dio}); + + Future> post({ + required Map param, + required String service, + String? token, + }) async { + try { + final response = await dio.post( + service, + data: jsonEncode(param), + options: Options( + headers: token != null + ? { + HttpHeaders.contentTypeHeader: "application/json", + HttpHeaders.authorizationHeader: "Bearer $token", + } + : { + HttpHeaders.contentTypeHeader: "application/json", + }, + contentType: "application/json", + ), + ); + if (response.statusCode != 200) { + throw BaseRepositoryException( + message: "Invalid Http Response ${response.statusCode}", + ); + } + Map jsonData = jsonDecode(response.data); + if (jsonData["status"] != "OK") { + throw BaseRepositoryException( + message: jsonData["message"], + ); + } else { + return jsonData; + } + } on DioException catch (e) { + throw BaseRepositoryException(message: e.message); + } on SocketException catch (e) { + throw BaseRepositoryException(message: e.message); + } on BaseRepositoryException catch (e) { + throw BaseRepositoryException(message: e.message); + } + } + + Future> get({ + required String service, + String? token, + }) async { + try { + final response = await dio.get( + service, + options: Options( + headers: token != null + ? { + HttpHeaders.contentTypeHeader: "application/json", + HttpHeaders.authorizationHeader: "Bearer $token", + } + : { + HttpHeaders.contentTypeHeader: "application/json", + }, + contentType: "application/json", + ), + ); + if (response.statusCode != 200) { + throw BaseRepositoryException( + message: "Invalid Http Response ${response.statusCode}", + ); + } + Map jsonData = jsonDecode(response.data); + if (jsonData["status"] != "OK") { + throw BaseRepositoryException( + message: jsonData["message"], + ); + } else { + return jsonData; + } + } on DioException catch (e) { + throw BaseRepositoryException(message: e.message); + } on SocketException catch (e) { + throw BaseRepositoryException(message: e.message); + } on BaseRepositoryException catch (e) { + throw BaseRepositoryException(message: e.message); + } + } +} + +class BaseRepositoryException implements Exception { + final String? message; + BaseRepositoryException({ + required this.message, + }); +} diff --git a/lib/repository/dashboard_repository.dart b/lib/repository/dashboard_repository.dart new file mode 100644 index 0000000..55214fe --- /dev/null +++ b/lib/repository/dashboard_repository.dart @@ -0,0 +1,43 @@ +import 'package:mitra_corporate/model/chart_data_model.dart'; +import 'package:mitra_corporate/model/surat_jalan_model.dart'; + +import '../app/constant.dart'; +import 'base_repository.dart'; + +class DashboardRepository extends BaseRepository { + DashboardRepository({required super.dio}); + + Future getChartData({ + required String token, + required String filter, + required String company_id, + }) async { + final param = {"token": token, "filter": filter, "company_id": company_id}; + // print(param); + final service = "${Constant.baseUrl}dashboard/chartdata"; + final resp = await post(param: param, service: service); + final result = ChartDataModel.fromJson(resp['data']); + + return result; + } + + Future> getOrder( + {required String token, required String companyID}) async { + final param = {"token": token, "company_id": companyID}; + // print(param); + final service = "${Constant.baseUrl}dashboard/getdelivery"; + final resp = await post(param: param, service: service); + // print(resp); + List data = List.empty(growable: true); + if (resp['status'] == 'OK') { + resp['data'].forEach((e) { + final model = SuratJalanModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + + return data; + } +} diff --git a/lib/repository/order_repository.dart b/lib/repository/order_repository.dart new file mode 100644 index 0000000..db73455 --- /dev/null +++ b/lib/repository/order_repository.dart @@ -0,0 +1,72 @@ +import 'package:mitra_corporate/model/search_order_model.dart'; + +import '../app/constant.dart'; +import '../model/registration_model.dart'; +import 'base_repository.dart'; + +class OrderRepository extends BaseRepository { + OrderRepository({required super.dio}); + + Future searchOrder( + {required String token, + required String page, + required String keyword, + required String rpp, + required String startDate, + required String endDate, + required String companyID}) async { + final param = { + "token": token, + "keyword": keyword, + "page": page, + "rpp": rpp, + "company_id": companyID, + "start_date": startDate, + "end_date": endDate + }; + // print(param); + final service = "${Constant.baseUrl}order/getorder"; + final resp = await post(param: param, service: service); + SearchOrderModel data; + // print(resp); + if (resp['status'] == 'OK') { + data = SearchOrderModel.fromJson(resp['data']); + } else { + data = SearchOrderModel(); + } + + return data; + } + + Future editOrder({required RegistrationModel prm}) async { + final url = "${Constant.baseUrl}order/editorder"; + final resp = await post(param: prm.toJson(), service: url); + String number; + // print(resp['data']); + if (resp['status'] == 'OK') { + number = resp['data']; + } else { + number = ""; + } + + return number; + } + + Future cancelOrder({ + required String token, + required String orderID, + }) async { + var param = {"token": token, "id": orderID}; + final url = "${Constant.baseUrl}order/cancel"; + final resp = await post(param: param, service: url); + String data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = resp['data']; + } else { + data = ""; + } + + return data; + } +} diff --git a/lib/repository/patient_repository.dart b/lib/repository/patient_repository.dart new file mode 100644 index 0000000..2b1f588 --- /dev/null +++ b/lib/repository/patient_repository.dart @@ -0,0 +1,66 @@ +import 'package:mitra_corporate/model/search_patient_model.dart'; + +import '../app/constant.dart'; +import '../model/registration_model.dart'; +import 'base_repository.dart'; + +class PatientRepository extends BaseRepository { + PatientRepository({required super.dio}); + + Future search( + {required String token, + required String page, + required String keyword, + required String rpp, + required String companyID}) async { + final param = { + "token": token, + "keyword": keyword, + "page": page, + "rpp": rpp, + "company_id": companyID + }; + // print(param); + final service = "${Constant.baseUrl}patient/search"; + final resp = await post(param: param, service: service); + SearchPatientModel data; + // print(resp); + if (resp['status'] == 'OK') { + data = SearchPatientModel.fromJson(resp['data']); + } else { + data = SearchPatientModel(); + } + + return data; + } + + Future editPatient({required RegistrationModel prm}) async { + final url = "${Constant.baseUrl}Patient/editpatient"; + final resp = await post(param: prm.toJson(), service: url); + String msg; + // print(resp['data']); + if (resp['status'] == 'OK') { + msg = resp['data']; + } else { + msg = ""; + } + + return msg; + } + + Future deletePatient( + {required String patient_id, required String token}) async { + final prm = {"patient_id": patient_id, "token": token}; + final url = "${Constant.baseUrl}Patient/deletePatient"; + final resp = await post(param: prm, service: url); + String msg; + // print(resp['data']); + if (resp['status'] == 'OK') { + msg = resp['data']; + } else { + msg = ""; + } + + return msg; + } +} diff --git a/lib/repository/registration_repository.dart b/lib/repository/registration_repository.dart new file mode 100644 index 0000000..a823ae8 --- /dev/null +++ b/lib/repository/registration_repository.dart @@ -0,0 +1,86 @@ +import 'package:mitra_corporate/model/registration_filter_model.dart'; +import 'package:mitra_corporate/model/registration_model.dart'; +import 'package:mitra_corporate/model/test_model.dart'; + +import '../app/constant.dart'; +import 'base_repository.dart'; + +class RegistrationRepository extends BaseRepository { + RegistrationRepository({required super.dio}); + + Future> getFPP({ + required String mouID, + required String token, + }) async { + var param = {"mou_id": mouID, "token": token}; + // print(param); + final url = "${Constant.baseUrl}fpp/load"; + final resp = await post(param: param, service: url); + List data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = []; + resp['data'].forEach((e) { + final model = TestModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + + return data; + } + + Future getFilter({ + required String token, + }) async { + var param = {"token": token}; + final url = "${Constant.baseUrl}Registration/getfilter"; + final resp = await post(param: param, service: url); + RegistrationFilterModel data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = RegistrationFilterModel.fromJson(resp['data']); + } else { + data = RegistrationFilterModel(); + } + + return data; + } + + Future> getSpecimen({ + required String token, + required List> arrTest, + }) async { + var param = {"token": token, "arr_test": arrTest}; + final url = "${Constant.baseUrl}Registration/getsampletype"; + final resp = await post(param: param, service: url); + List data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = []; + resp['data'].forEach((e) { + final model = GetSpecimenModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + + return data; + } + + Future addOrder({required RegistrationModel prm}) async { + final url = "${Constant.baseUrl}Registration/addpatient"; + final resp = await post(param: prm.toJson(), service: url); + String number; + // print(resp['data']); + if (resp['status'] == 'OK') { + number = resp['data']['orderNumber']; + } else { + number = ""; + } + + return number; + } +} diff --git a/lib/repository/surat_jalan_repository.dart b/lib/repository/surat_jalan_repository.dart new file mode 100644 index 0000000..4901e9b --- /dev/null +++ b/lib/repository/surat_jalan_repository.dart @@ -0,0 +1,222 @@ +import 'package:mitra_corporate/model/create_surat_jalan_model.dart'; +import 'package:mitra_corporate/model/delivery_type_model.dart'; +import 'package:mitra_corporate/model/destination_model.dart'; +import 'package:mitra_corporate/model/order_model.dart'; +import 'package:mitra_corporate/model/registration_filter_model.dart'; +import 'package:mitra_corporate/model/surat_jalan_detail_model.dart'; + +import '../app/constant.dart'; +import '../model/surat_jalan_model.dart'; +import 'base_repository.dart'; + +class SuratJalanRepository extends BaseRepository { + SuratJalanRepository({required super.dio}); + + Future> getDeliveryType({ + required String token, + }) async { + var param = {"token": token}; + final url = "${Constant.baseUrl}deliveryorder/getdeliverytype"; + final resp = await post(param: param, service: url); + List data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = []; + resp['data'].forEach((e) { + final model = DeliveryTypeModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + + return data; + } + + Future> getOrder({ + required String token, + required String company_id, + required String regional_id, + }) async { + var param = { + "token": token, + "company_id": company_id, + "regional_id": regional_id + }; + final url = "${Constant.baseUrl}deliveryorder/getorder"; + final resp = await post(param: param, service: url); + List data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = []; + resp['data'].forEach((e) { + final model = OrderModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + + return data; + } + + Future createSuratJalan({required CreateSuratJalanModel prm}) async { + final url = "${Constant.baseUrl}deliveryorder/addDelivery"; + final resp = await post(param: prm.toJson(), service: url); + String number; + // print(resp['data']); + if (resp['status'] == 'OK') { + number = resp['data']['orderNumber']; + } else { + number = ""; + } + + return number; + } + + Future getSuratJalan({ + required String token, + required String company_id, + required String rpp, + required String keyword, + required String page, + required String startDate, + required String endDate, + required String dateType, + }) async { + var param = { + "token": token, + "page": page, + "keyword": keyword, + "rpp": rpp, + "company_id": company_id, + "start_date": startDate, + "end_date": endDate, + "date_type": dateType + }; + final url = "${Constant.baseUrl}deliveryorder/getdelivery"; + final resp = await post(param: param, service: url); + SearchSuratJalanModel data; + // print(resp); + if (resp['status'] == 'OK') { + data = SearchSuratJalanModel.fromJson(resp['data']); + } else { + data = SearchSuratJalanModel(); + } + + return data; + } + + Future> getSuratJalanSetail({ + required String token, + required String deliveryID, + }) async { + var param = {"token": token, "id": deliveryID}; + final url = "${Constant.baseUrl}deliveryorder/detaildelivery"; + final resp = await post(param: param, service: url); + List data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = []; + resp['data'].forEach((e) { + final model = SuratJalanDetailModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + + return data; + } + + Future cancelSuratJalan({ + required String token, + required String deliveryID, + }) async { + var param = {"token": token, "id": deliveryID}; + final url = "${Constant.baseUrl}deliveryorder/cancel"; + final resp = await post(param: param, service: url); + String data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = resp['data']; + } else { + data = ""; + } + + return data; + } + + Future sendSuratJalan({ + required String token, + required String deliveryID, + }) async { + var param = {"token": token, "id": deliveryID}; + final url = "${Constant.baseUrl}deliveryorder/send"; + final resp = await post(param: param, service: url); + String data; + // print(resp['data']); + if (resp['status'] == 'OK') { + data = resp['data']; + } else { + data = ""; + } + + return data; + } + + Future> getDestination({ + required String token, + }) async { + var param = {"token": token}; + final url = "${Constant.baseUrl}deliveryorder/getdestination"; + final resp = await post(param: param, service: url); + List data = List.empty(growable: true); + if (resp['status'] == 'OK') { + data = []; + resp['data'].forEach((e) { + final model = DestinationModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + + return data; + } + + Future> getRegional({ + required String token, + }) async { + var param = {"token": token}; + final url = "${Constant.baseUrl}deliveryorder/getregional"; + final resp = await post(param: param, service: url); + List data = List.empty(growable: true); + if (resp['status'] == 'OK') { + data = []; + resp['data'].forEach((e) { + final model = RegionalModel.fromJson(e); + data.add(model); + }); + } else { + data = []; + } + + return data; + } + + Future sendQrCode( + {required String token, required List arrOrderID}) async { + var param = {"token": token, 'arr_order_id': arrOrderID}; + final url = "${Constant.baseUrl}deliveryorder/sendqrcode"; + final resp = await post(param: param, service: url); + String data = ""; + if (resp['status'] == 'OK') { + data = "OK"; + } else { + data = "ERROR"; + } + + return data; + } +} diff --git a/lib/screen/dashboard/chart.dart b/lib/screen/dashboard/chart.dart new file mode 100644 index 0000000..c9fcee7 --- /dev/null +++ b/lib/screen/dashboard/chart.dart @@ -0,0 +1,232 @@ +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/app/constant.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/screen/dashboard/chart_provider.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../widgets/custom_snackbar_widget.dart'; + +class ChartScreen extends HookConsumerWidget { + const ChartScreen({super.key, required this.filter}); + final ValueNotifier filter; + @override + Widget build(BuildContext context, WidgetRef ref) { + // N = New, S= Send, P= Parsial, D=Done, + final auth = ref.watch(authProvider); + final chartLoading = useState(false); + + final total = useState>(List.empty()); + final dikirim = useState>(List.empty()); + final dikonfirmasi = useState>(List.empty()); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + var fltr = "month"; + if (filter.value.toLowerCase() == "bulan") { + fltr = "month"; + } + if (filter.value.toLowerCase() == "tahun") { + fltr = "year"; + } + ref.read(ChartDataProvider.notifier).ChartData( + token: auth?.token ?? "", + filter: fltr, + company_id: auth?.mUserMCompanyID ?? ""); + }); + return () {}; + }, [filter.value]); + + ref.listen(ChartDataProvider, (prev, next) async { + if (next is ChartDataStateLoading) { + chartLoading.value = true; + } else if (next is ChartDataStateError) { + chartLoading.value = false; + + SanckbarWidget(context, next.message ?? "", snackbarType.error); + Constant.autoLogout(context: context, msg: next.message ?? ""); + } else if (next is ChartDataStateDone) { + total.value = next.model.t ?? []; + dikirim.value = next.model.s ?? []; + dikonfirmasi.value = next.model.y ?? []; + + chartLoading.value = false; + } + }); + + return Scaffold( + body: chartLoading.value + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 40)) + : Padding( + padding: const EdgeInsets.all(20), + child: LineChart( + LineChartData( + gridData: FlGridData( + show: true, + ), + borderData: FlBorderData( + show: true, + border: Border( + top: BorderSide.none, + right: BorderSide.none, + left: BorderSide(color: Colors.grey.shade500), + bottom: BorderSide(color: Colors.grey.shade500))), + backgroundColor: Colors.white, + minX: 0, + minY: 0, + // maxX: 31, + titlesData: FlTitlesData( + rightTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + topTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false)), + leftTitles: AxisTitles( + axisNameSize: 30, + axisNameWidget: Text( + "Total", + style: Constant.caption1_400(context: context), + ), + sideTitles: SideTitles( + showTitles: true, + reservedSize: 32, + interval: 10, + getTitlesWidget: (double value, TitleMeta meta) { + return SideTitleWidget( + axisSide: meta.axisSide, + space: 10, + child: Text( + value.toInt().toString(), + style: + Constant.caption1_400(context: context), + ), + ); + }, + )), + bottomTitles: AxisTitles( + axisNameSize: 30, + axisNameWidget: Container( + child: Text( + filter.value == "Bulan" ? "Hari" : "Bulan", + style: Constant.caption1_400(context: context), + ), + ), + sideTitles: SideTitles( + showTitles: true, + reservedSize: 32, + interval: 1, + getTitlesWidget: (double value, TitleMeta meta) { + return SideTitleWidget( + axisSide: meta.axisSide, + space: 10, + child: Text( + style: Constant.caption1_400( + context: context), + value.toInt().toString(), + )); + }, + ))), + lineBarsData: [ + // LineChartBarData( + // isCurved: true, + // curveSmoothness: 0.20, + // color: Colors.red, + // barWidth: 4, + // isStrokeCapRound: true, + // dotData: FlDotData( + // checkToShowDot: (spot, barData) { + // if (pending.value.length == 1) { + // return true; + // } else { + // return false; + // } + // }, + // ), + // belowBarData: BarAreaData(show: false), + // spots: pending.value.map((e) { + // var sp = e.split("|"); + // return FlSpot( + // double.parse(sp[0]), double.parse(sp[1])); + // }).toList()), + LineChartBarData( + isCurved: true, + curveSmoothness: 0.2, + color: Colors.purple, + barWidth: 4, + isStrokeCapRound: true, + dotData: FlDotData( + checkToShowDot: (spot, barData) { + if (dikirim.value.length == 1) { + return true; + } else { + return false; + } + }, + ), + belowBarData: BarAreaData(show: false), + spots: dikirim.value.map((e) { + var sp = e.split("|"); + return FlSpot( + double.parse(sp[0]), double.parse(sp[1])); + }).toList()), + LineChartBarData( + isCurved: true, + curveSmoothness: 0.1, + color: Colors.blue, + barWidth: 4, + isStrokeCapRound: true, + dotData: FlDotData( + checkToShowDot: (spot, barData) { + if (total.value.length == 1) { + return true; + } else { + return false; + } + }, + ), + belowBarData: BarAreaData(show: false), + spots: total.value.map((e) { + var sp = e.split("|"); + return FlSpot( + double.parse(sp[0]), double.parse(sp[1])); + }).toList()), + LineChartBarData( + isCurved: true, + curveSmoothness: 0.20, + color: Colors.green, + barWidth: 4, + isStrokeCapRound: true, + dotData: FlDotData( + checkToShowDot: (spot, barData) { + if (dikonfirmasi.value.length == 1) { + return true; + } else { + return false; + } + }, + ), + belowBarData: BarAreaData(show: false), + spots: dikonfirmasi.value.map((e) { + var sp = e.split("|"); + return FlSpot( + double.parse(sp[0]), double.parse(sp[1])); + }).toList()), + ], + extraLinesData: ExtraLinesData(horizontalLines: [ + HorizontalLine( + y: 0, + color: Colors.white, + strokeWidth: 1, + dashArray: [5]) + ])), + // swapAnimationDuration: Duration(milliseconds: 150), + // swapAnimationCurve: Curves.linear, + ), + ), + ); + } +} diff --git a/lib/screen/dashboard/chart_provider.dart b/lib/screen/dashboard/chart_provider.dart new file mode 100644 index 0000000..eefb13c --- /dev/null +++ b/lib/screen/dashboard/chart_provider.dart @@ -0,0 +1,69 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/chart_data_model.dart'; +import 'package:mitra_corporate/repository/dashboard_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +// 3. state provider +final ChartDataProvider = + StateNotifierProvider( + (ref) => ChartDataNotifier(ref: ref)); + +// 2. notifier +class ChartDataNotifier extends StateNotifier { + final Ref ref; + ChartDataNotifier({required this.ref}) : super(ChartDataStateInit()); + void ChartData( + {required String token, + required String filter, + required String company_id, + bool isRememberMe = false}) async { + try { + state = ChartDataStateLoading(); + final resp = await DashboardRepository(dio: ref.read(dioProvider)) + .getChartData(token: token, filter: filter, company_id: company_id); + + state = ChartDataStateDone(model: resp); + + // print(shared.getString(Constant.bearerName)); + } catch (e) { + if (e is BaseRepositoryException) { + state = ChartDataStateError(message: e.message ?? ""); + } else { + state = ChartDataStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class ChartDataState extends Equatable { + final DateTime date; + const ChartDataState(this.date); + @override + List get props => [date]; +} + +class ChartDataStateInit extends ChartDataState { + ChartDataStateInit() : super(DateTime.now()); +} + +class ChartDataStateLoading extends ChartDataState { + ChartDataStateLoading() : super(DateTime.now()); +} + +class ChartDataStateError extends ChartDataState { + final String? message; + ChartDataStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class ChartDataStateDone extends ChartDataState { + final ChartDataModel model; + ChartDataStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/dashboard/dashboard_delivery_provider.dart b/lib/screen/dashboard/dashboard_delivery_provider.dart new file mode 100644 index 0000000..bbf0cec --- /dev/null +++ b/lib/screen/dashboard/dashboard_delivery_provider.dart @@ -0,0 +1,72 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'package:mitra_corporate/model/surat_jalan_model.dart'; + +import 'package:mitra_corporate/repository/dashboard_repository.dart'; + +import '../../provider/dio_provider.dart'; + +import '../../repository/base_repository.dart'; + +// 3. state provider +final DashboardDeliveryProvider = + StateNotifierProvider( + (ref) => DashboardDeliveryNotifier(ref: ref)); + +// 2. notifier +class DashboardDeliveryNotifier extends StateNotifier { + final Ref ref; + DashboardDeliveryNotifier({required this.ref}) + : super(DashboardDeliveryStateInit()); + void DashboardDelivery( + {required String token, + required String company_id, + bool isRememberMe = false}) async { + try { + state = DashboardDeliveryStateLoading(); + final resp = await DashboardRepository(dio: ref.read(dioProvider)) + .getOrder(token: token, companyID: company_id); + + state = DashboardDeliveryStateDone(model: resp); + + // print(shared.getString(Constant.bearerName)); + } catch (e) { + if (e is BaseRepositoryException) { + state = DashboardDeliveryStateError(message: e.message ?? ""); + } else { + state = DashboardDeliveryStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class DashboardDeliveryState extends Equatable { + final DateTime date; + const DashboardDeliveryState(this.date); + @override + List get props => [date]; +} + +class DashboardDeliveryStateInit extends DashboardDeliveryState { + DashboardDeliveryStateInit() : super(DateTime.now()); +} + +class DashboardDeliveryStateLoading extends DashboardDeliveryState { + DashboardDeliveryStateLoading() : super(DateTime.now()); +} + +class DashboardDeliveryStateError extends DashboardDeliveryState { + final String? message; + DashboardDeliveryStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class DashboardDeliveryStateDone extends DashboardDeliveryState { + final List model; + DashboardDeliveryStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/dashboard/dashboard_screen.dart b/lib/screen/dashboard/dashboard_screen.dart new file mode 100644 index 0000000..c3c66f7 --- /dev/null +++ b/lib/screen/dashboard/dashboard_screen.dart @@ -0,0 +1,213 @@ +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/screen/dashboard/chart.dart'; +// import 'package:syncfusion_flutter_charts/charts.dart'; +import '../../app/constant.dart'; +import '../../provider/menu_provider.dart'; +import '../../widgets/header.dart'; + +class DashboardScreen extends HookConsumerWidget { + const DashboardScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final isExpand = ref.watch(sideBarExpandProvider); + List chartType = ['Tahun', 'Bulan']; + final selectedChartType = useState("Tahun"); + + return Scaffold( + body: Column( + children: [ + Header(), + Padding( + padding: EdgeInsets.all(32.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + // color: Colors.red, + alignment: Alignment.centerLeft, + child: Text('Dashboard', + style: Constant.h3_400(context: context) + .copyWith(color: Constant.textBlack)), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 40), + ), + //Statistik + Material( + // elevation: 5, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8)), + color: Colors.white, + child: Container( + padding: EdgeInsets.all(20), + // width: Constant.getActualX(context: context, x: 1080), + height: Constant.getActualY(context: context, y: 720), + child: Column( + children: [ + Row( + children: [ + Text( + "Statistik", + style: Constant.h4_600(context: context), + ), + const Spacer(), + // Text( + // "Bulan", + // style: Constant.caption1_400(context: context), + // ), + // Icon(EvaIcons.arrowIosDownward, size: 12), + DropdownButton( + value: selectedChartType.value, + icon: Icon(EvaIcons.arrowIosDownward, size: 12), + elevation: 16, + style: Constant.caption1_400(context: context) + .copyWith(color: Colors.black), + underline: Container( + height: 0, + ), + focusColor: Colors.white, + onChanged: (value) { + // This is called when the user selects an item. + selectedChartType.value = value!; + }, + items: chartType.map>( + (String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ) + ], + ), + SizedBox( + height: + Constant.getActualX(context: context, x: 4)), + // Row( + // children: [ + // Text( + // "Last Update 1 Juli", + // style: Constant.caption1_400(context: context), + // ), + // ], + // ), + // Image.asset( + // 'images/grafik.png', + // width: Constant.getActualX(context: context, x: 1039), + // height: Constant.getActualY(context: context, y: 241), + // ), + + SizedBox( + // width: + // Constant.getActualX(context: context, x: 1039), + height: + Constant.getActualY(context: context, y: 550), + child: Row( + children: [ + Expanded( + flex: 9, + child: ChartScreen( + filter: selectedChartType, + )), + Expanded(flex: 1, child: Container()), + Expanded( + flex: 2, + child: Container( + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Row( + children: [ + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: Colors.blue, + borderRadius: + BorderRadius.circular( + 5)), + margin: + EdgeInsets.only(right: 10), + ), + Text( + "Total Order", + style: Constant.caption1_400( + context: context), + ) + ], + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 16), + ), + Row( + children: [ + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: Colors.purple, + borderRadius: + BorderRadius.circular( + 5)), + margin: + EdgeInsets.only(right: 10), + ), + Text( + "Dikirim", + style: Constant.caption1_400( + context: context), + ) + ], + ), + SizedBox( + height: Constant.getActualY( + context: context, y: 16), + ), + Row( + children: [ + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: Colors.green, + borderRadius: + BorderRadius.circular( + 5)), + margin: + EdgeInsets.only(right: 10), + ), + Text( + "Dikonfirmasi", + style: Constant.caption1_400( + context: context), + ) + ], + ), + ], + ), + )), + ], + ), + ) + ], + )), + ), + // SizedBox( + // height: Constant.getActualY(context: context, y: 40), + // ), + // //Pengiriman terbaru + // DashboardTabel() + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/screen/dashboard/dashboard_tabel.dart b/lib/screen/dashboard/dashboard_tabel.dart new file mode 100644 index 0000000..309ced4 --- /dev/null +++ b/lib/screen/dashboard/dashboard_tabel.dart @@ -0,0 +1,283 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/model/surat_jalan_model.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../provider/menu_provider.dart'; +import '../../widgets/custom_snackbar_widget.dart'; +import 'dashboard_delivery_provider.dart'; + +class DashboardTabel extends HookConsumerWidget { + const DashboardTabel({ + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final auth = ref.read(authProvider); + final dataList = useState>(List.empty()); + final loading = useState(false); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + ref.read(DashboardDeliveryProvider.notifier).DashboardDelivery( + token: auth?.token ?? "", company_id: auth?.mUserMCompanyID ?? ""); + }); + return () {}; + }, []); + ref.listen(DashboardDeliveryProvider, (prev, next) async { + if (next is DashboardDeliveryStateLoading) { + loading.value = true; + } else if (next is DashboardDeliveryStateError) { + loading.value = false; + + SanckbarWidget(context, next.message ?? "", snackbarType.error); + Constant.autoLogout(context: context, msg: next.message ?? ""); + } else if (next is DashboardDeliveryStateDone) { + dataList.value = next.model; + loading.value = false; + } + }); + return Material( + elevation: 5, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + color: Colors.white, + child: SizedBox( + height: Constant.getActualY(context: context, y: 370), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Text('Pengiriman Terbaru', + style: Constant.h4_600(context: context) + .copyWith(color: Constant.textBlack)), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 8)), + child: Column( + children: [ + Container( + decoration: BoxDecoration( + color: Constant.grey_200, + borderRadius: BorderRadius.circular(8)), + height: Constant.getActualY(context: context, y: 56), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 2, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 12)), + child: Text( + "Tanggal", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + Expanded( + flex: 2, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 12)), + child: Text( + "Nomor Surat Jalan", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + Expanded( + flex: 2, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 12)), + child: Text( + "Staff PIC", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + Expanded( + flex: 2, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 12)), + child: Text( + "Tipe Pengiriman", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + Expanded( + flex: 2, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 12)), + child: Text( + "Tujuan", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + Expanded( + flex: 2, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 12)), + child: Text( + "Status", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + ], + ), + ) + ], + ), + ), + Container( + height: Constant.getActualY(context: context, y: 160), + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 8)), + child: loading.value + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 40)) + : ListView( + children: dataList.value + .map((e) => Container( + margin: EdgeInsets.symmetric( + vertical: Constant.getActualY( + context: context, y: 10)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 2, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 12)), + child: SelectableText(e.date ?? "", + style: Constant.body3_600( + context: context)), + )), + Expanded( + flex: 2, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 12)), + child: SelectableText( + e.orderNumber ?? "", + style: Constant.body3_600( + context: context)), + )), + Expanded( + flex: 2, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 12)), + child: SelectableText(e.pic ?? "", + style: Constant.body3_600( + context: context)), + )), + Expanded( + flex: 2, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 12)), + child: SelectableText(e.type ?? "", + style: Constant.body3_600( + context: context)), + )), + Expanded( + flex: 2, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 12)), + child: SelectableText( + e.destination ?? "", + style: Constant.body3_600( + context: context)), + )), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.bottomLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 12)), + child: Chip( + backgroundColor: + Constant.yellow_016, + label: SelectableText( + "Proses", + style: Constant.caption1_600( + context: context) + .copyWith( + color: Constant + .textYellow), + ), + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(8)), + ) + // Text("Status", + // style: Constant.body3_600(context: context)), + )), + ], + ), + )) + .toList(), + ), + ), + Divider(), + Container( + padding: EdgeInsets.only(right: 10), + height: Constant.getActualY(context: context, y: 60), + child: Row( + children: [ + Spacer(), + TextButton( + onPressed: () { + ref.read(currentMenuProvider.notifier).state = 3; + }, + child: Row( + children: [ + Text( + "View All", + style: Constant.body3_600(context: context) + .copyWith(color: Colors.black), + ), + Icon( + Icons.arrow_forward_ios_rounded, + color: Colors.black, + size: 17, + ) + ], + )) + ], + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/screen/home/home_screen.dart b/lib/screen/home/home_screen.dart new file mode 100644 index 0000000..3aac773 --- /dev/null +++ b/lib/screen/home/home_screen.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/screen/dashboard/dashboard_screen.dart'; +import 'package:mitra_corporate/screen/registrasi/registrasi_pasien_screen.dart'; +import 'package:mitra_corporate/screen/result/detail_result_screen.dart'; +import 'package:mitra_corporate/screen/result/result_screen.dart'; +import 'package:mitra_corporate/screen/surat_jalan/surat_jalan_detail_screen.dart'; +import 'package:mitra_corporate/screen/surat_jalan/surat_jalan_screen.dart'; +import 'package:mitra_corporate/widgets/side_menu.dart'; + +import '../../provider/menu_provider.dart'; +import '../order/order_screen.dart'; + +class HomeScreen extends HookConsumerWidget { + const HomeScreen({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final currentMenu = ref.watch(currentMenuProvider); + // getPref() async { + // final SharedPreferences prefs = await SharedPreferences.getInstance(); + // final String? getData = prefs.getString(Constant.tokenName); + // // print(getData); + // if (getData == null) { + // Timer(Duration(seconds: 1), () { + // Navigator.pushNamed(context, loginRoute); + // }); + // } + // } + + // useEffect(() { + // WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + // getPref(); + // }); + // return () {}; + // }, []); + return Scaffold( + body: Container( + child: Row( + children: [ + SideMenu(), + if (currentMenu == 1) Expanded(child: DashboardScreen()), + if (currentMenu == 2) Expanded(child: RegistrasiPasienScreen()), + if (currentMenu == 3) Expanded(child: SuratJalanScreen()), + if (currentMenu == 4) Expanded(child: ResultScreen()), + if (currentMenu == 5) Expanded(child: SuratJalanDetailScreen()), + if (currentMenu == 6) Expanded(child: OrderScreen()), + if (currentMenu == 7) Expanded(child: DetailResultScreen()), + + // Container( + // child: Text("a"), + // ) + ], + ), + ), + ); + } +} diff --git a/lib/screen/login/change_password_provider.dart b/lib/screen/login/change_password_provider.dart new file mode 100644 index 0000000..3de858a --- /dev/null +++ b/lib/screen/login/change_password_provider.dart @@ -0,0 +1,72 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/auth_repository.dart'; +import '../../repository/base_repository.dart'; + +// 3. state provider +final ChangePasswordProvider = + StateNotifierProvider( + (ref) => ChangePasswordNotifier(ref: ref)); + +// 2. notifier +class ChangePasswordNotifier extends StateNotifier { + final Ref ref; + ChangePasswordNotifier({required this.ref}) + : super(ChangePasswordStateInit()); + void ChangePassword({ + required String token, + required String current_password, + required String new_password, + required String password_confirmation, + }) async { + try { + state = ChangePasswordStateLoading(); + final resp = await AuthRepository(dio: ref.read(dioProvider)) + .changePassword( + token: token, + current_password: current_password, + new_password: new_password, + password_confirmation: password_confirmation); + + // print(resp); + state = ChangePasswordStateDone(message: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = ChangePasswordStateError(message: e.message ?? ""); + } else { + state = ChangePasswordStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class ChangePasswordState extends Equatable { + final DateTime date; + const ChangePasswordState(this.date); + @override + List get props => [date]; +} + +class ChangePasswordStateInit extends ChangePasswordState { + ChangePasswordStateInit() : super(DateTime.now()); +} + +class ChangePasswordStateLoading extends ChangePasswordState { + ChangePasswordStateLoading() : super(DateTime.now()); +} + +class ChangePasswordStateError extends ChangePasswordState { + final String message; + ChangePasswordStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class ChangePasswordStateDone extends ChangePasswordState { + final String message; + ChangePasswordStateDone({ + required this.message, + }) : super(DateTime.now()); +} diff --git a/lib/screen/login/login_box.dart b/lib/screen/login/login_box.dart new file mode 100644 index 0000000..0781061 --- /dev/null +++ b/lib/screen/login/login_box.dart @@ -0,0 +1,301 @@ +import 'dart:convert'; + +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/app/route.dart'; +import 'package:mitra_corporate/model/auth_model.dart'; +import 'package:mitra_corporate/screen/login/login_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../model/registration_filter_model.dart'; +import '../../provider/auth_provider.dart'; +import '../../widgets/custom_snackbar_widget.dart'; +import '../../widgets/custom_text_field.dart'; +import '../surat_jalan/get_regional_provider.dart'; + +class LoginBox extends HookConsumerWidget { + const LoginBox({super.key}); + + // bool? _value = false; + // bool? remember = false; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final remember = useState(false); + final obsecureText = useState(true); + // final companyIdCtr = useTextEditingController(); + final usernameCtr = useTextEditingController(); + final passwordCtr = useTextEditingController(); + final regionalList = useState>(List.empty()); + // final selectedRegional = useState(RegionalModel()); + final regionalKey = useState(1); + final regionalLoading = useState(false); + final isLoading = useState(false); + final errorMsg = useState(""); + getPref() async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + final String? getData = prefs.getString(Constant.tokenName); + print(getData); + if (getData != null) { + ref.read(authProvider.notifier).state = + AuthModel.fromJson(jsonDecode(getData)); + Navigator.pushNamed(context, homeRoute); + } + } + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + // ref.read(GetRegionalProvider.notifier).getRegional(token: ""); + getPref(); + }); + return () {}; + }, []); + + login() { + if ( + // companyIdCtr.text.isEmpty || + usernameCtr.text.isEmpty || passwordCtr.text.isEmpty + // selectedRegional.value.regionalId == null + ) { + SanckbarWidget( + context, "Username dan Password harus diisi", snackbarType.warning); + // toastification.show( + + // context: context, + // title: 'Hello, world!', + // autoCloseDuration: const Duration(seconds: 5), + // ); + } else { + ref.read(loginProvider.notifier).login( + username: usernameCtr.text, + password: passwordCtr.text, + // company_id: companyIdCtr.text, + // regional_id: selectedRegional.value.regionalId ?? "", + isRememberMe: remember.value); + } + } + + // aksi login + ref.listen(loginProvider, (prev, next) async { + if (next is LoginStateLoading) { + isLoading.value = true; + } else if (next is LoginStateError) { + isLoading.value = false; + errorMsg.value = next.message ?? ""; + SanckbarWidget(context, errorMsg.value, snackbarType.error); + } else if (next is LoginStateDone) { + isLoading.value = false; + if (next.model.token != null) { + Navigator.pushNamed(context, homeRoute); + } else { + SanckbarWidget(context, "Login Gagal", snackbarType.error); + } + } + }); + ref.listen( + GetRegionalProvider, + (previous, next) { + if (next is GetRegionalStateInit) { + regionalLoading.value = true; + } else if (next is GetRegionalStateLoading) { + regionalLoading.value = true; + } else if (next is GetRegionalStateError) { + SanckbarWidget(context, next.message, snackbarType.error); + regionalLoading.value = false; + } else if (next is GetRegionalStateDone) { + // print(jsonEncode(next.model)); + regionalList.value = next.model; + regionalKey.value = regionalKey.value + 1; + regionalLoading.value = false; + } + }, + ); + + return Column(children: [ + //Selamat datang + SizedBox( + width: Constant.getActualX(context: context, x: 320), + // height: Constant.getActualY(context: context, y: 52), + // color: Colors.blueAccent, + child: Text("Selamat Datang", + style: Constant.h1_700(context: context) + .copyWith(color: Constant.textBlack)), + ), + + SizedBox( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 28), + // color: Colors.blueAccent, + + child: Text("Silahkan login untuk masuk", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.textGrey)), + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 48), + ), + // SizedBox( + // width: Constant.getActualX(context: context, x: 320), + // // height: Constant.getActualY(context: context, y: 56), + // child: DropdownMenu( + // enableFilter: true, + // enableSearch: true, + // key: ValueKey(regionalKey.value), + // menuHeight: Constant.getActualY(context: context, y: 300), + // initialSelection: selectedRegional.value, + // trailingIcon: regionalLoading.value + // ? LoadingAnimationWidget.discreteCircle( + // color: Constant.primaryRed, size: 20) + // : null, + // width: Constant.getActualX(context: context, x: 320), + // hintText: "Regional", + // textStyle: Constant.body2_400(context: context), + // inputDecorationTheme: InputDecorationTheme( + // border: OutlineInputBorder( + // borderSide: BorderSide(color: Constant.primaryRed), + // borderRadius: BorderRadius.circular(8), + // )), + // label: const Text( + // "Regional", + // maxLines: 1, + // overflow: TextOverflow.ellipsis, + // ), + // onSelected: (value) { + // // onSelectSaluation(value!); + // if (value!.regionalId != selectedRegional.value.regionalId) { + // print("change"); + // selectedRegional.value = value; + // } + // }, + // dropdownMenuEntries: regionalList.value + // .map>((e) => DropdownMenuEntry( + // value: e, + // label: e.regionalName ?? "", + // )) + // .toList()), + // ), + // SizedBox( + // height: Constant.getActualY(context: context, y: 20), + // ), + //Username + + // SizedBox( + // width: Constant.getActualX(context: context, x: 320), + // // height: Constant.getActualY(context: context, y: 56), + // child: CustomTextField( + // controller: companyIdCtr, + // hintText: "Company ID", + // labelText: "Company ID", + // ), + // ), + // SizedBox( + // height: Constant.getActualY(context: context, y: 20), + // ), + //Username + SizedBox( + width: Constant.getActualX(context: context, x: 320), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + controller: usernameCtr, + hintText: "Username", + labelText: "Username", + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + //Password + SizedBox( + width: Constant.getActualX(context: context, x: 320), + // height: Constant.getActualY(context: context, y: 56), + child: CustomPasswordField( + controller: passwordCtr, + isPassword: obsecureText, + hintText: "Password", + labelText: "Password", + suffixIcon: Icon(EvaIcons.eyeOffOutline), + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 24), + child: Row( + children: [ + Center( + child: Transform.scale( + scale: 0.8, + child: Checkbox( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4)), + value: remember.value, + onChanged: (value) { + remember.value = value!; + }), + ), + ), + Text('Remember Me', + style: Constant.body3_400(context: context) + .copyWith(color: Constant.textBlack)), + Spacer(), + // SizedBox( + // child: Text("Forgot Password?", + // style: Constant.body3_400(context: context) + // .copyWith(color: Constant.textBlue)), + // ) + ], + ), + ), + SizedBox(height: Constant.getActualY(context: context, y: 56)), + SizedBox( + width: Constant.getActualX(context: context, x: 320), + height: Constant.getActualY(context: context, y: 56), + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(8), + // gradient: LinearGradient( + // colors: [Constant.buttonPrimary1, Constant.buttonPrimary2])), + child: ElevatedButton( + onPressed: () { + isLoading.value ? null : login(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Constant.primaryRed, + // shadowColor: Colors.transparent + ), + child: isLoading.value + ? Center( + child: LoadingAnimationWidget.staggeredDotsWave( + color: Colors.white, size: 30), + ) + : Text('Login', + style: Constant.button_large(context: context) + .copyWith(color: Constant.textWhite)), + ), + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Text(Constant.version, + style: Constant.body3_600(context: context).copyWith( + color: Constant.textGrey, + )) + // SizedBox( + // width: Constant.getActualX(context: context, x: 320), + // // height: Constant.getActualY(context: context, y: 24), + // child: Align( + // alignment: Alignment.center, + // child: Text( + // 'Problem Login ? ', + // style: Constant.caption1_400(context: context) + // .copyWith(color: Constant.textBlue), + // ), + // ), + // ) + ]); + } +} diff --git a/lib/screen/login/login_provider.dart b/lib/screen/login/login_provider.dart new file mode 100644 index 0000000..74a2169 --- /dev/null +++ b/lib/screen/login/login_provider.dart @@ -0,0 +1,96 @@ +import 'dart:convert'; + +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../app/constant.dart'; + +import '../../model/auth_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/auth_repository.dart'; +import '../../repository/base_repository.dart'; + +// 3. state provider +final loginProvider = StateNotifierProvider( + (ref) => LoginNotifier(ref: ref)); + +// 2. notifier +class LoginNotifier extends StateNotifier { + final Ref ref; + LoginNotifier({required this.ref}) : super(LoginStateInit()); + void login( + {required String username, + required String password, + // required String company_id, + // required String regional_id, + bool isRememberMe = false}) async { + try { + state = LoginStateLoading(); + final resp = await AuthRepository(dio: ref.read(dioProvider)).login( + username: username, + password: password, + // company_id: company_id, + // regional_id: regional_id + ); + + state = LoginStateDone(model: resp); + //Simpan ke token jk remember me + if (isRememberMe == true) { + final shared = await SharedPreferences.getInstance(); + resp.isRememberMe = isRememberMe; + final tokenEncode = jsonEncode(resp); + await shared.setString(Constant.tokenName, tokenEncode); + } else { + // share pref di remove karena remember me tidak di centang + final shared = await SharedPreferences.getInstance(); + await shared.remove("usernameX"); + await shared.remove("passwordX"); + await shared.remove("isRememberMeX"); + await shared.remove(Constant.tokenName); + } + + resp.isRememberMe = isRememberMe; + ref.read(authProvider.notifier).state = resp; + + // print(shared.getString(Constant.bearerName)); + } catch (e) { + if (e is BaseRepositoryException) { + state = LoginStateError(message: e.message ?? ""); + } else { + state = LoginStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class LoginState extends Equatable { + final DateTime date; + const LoginState(this.date); + @override + List get props => [date]; +} + +class LoginStateInit extends LoginState { + LoginStateInit() : super(DateTime.now()); +} + +class LoginStateLoading extends LoginState { + LoginStateLoading() : super(DateTime.now()); +} + +class LoginStateError extends LoginState { + final String? message; + LoginStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class LoginStateDone extends LoginState { + final AuthModel model; + LoginStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/login/login_screen.dart b/lib/screen/login/login_screen.dart new file mode 100644 index 0000000..e8f8a95 --- /dev/null +++ b/lib/screen/login/login_screen.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:mitra_corporate/screen/login/login_box.dart'; + +import '../../app/constant.dart'; + +class LoginScreen extends StatelessWidget { + const LoginScreen({super.key}); + @override + Widget build(BuildContext context) { + return Material( + child: Stack( + children: [ + //image + Image.asset( + "images/pramita_login.png", + fit: BoxFit.fill, + width: Constant.getActualX(context: context, x: 1440), + height: Constant.getActualY(context: context, y: 1024), + ), + //login + Positioned( + top: Constant.getActualY(context: context, y: 200), + left: Constant.getActualX(context: context, x: 963), + width: Constant.getActualX(context: context, x: 320), + // height: Constant.getActualY(context: context, y: 533), + child: LoginBox(), + ) + ], + ), + ); + } +} diff --git a/lib/screen/login/logout_provider.dart b/lib/screen/login/logout_provider.dart new file mode 100644 index 0000000..da7f7a3 --- /dev/null +++ b/lib/screen/login/logout_provider.dart @@ -0,0 +1,64 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/auth_repository.dart'; +import '../../repository/base_repository.dart'; + +// 3. state provider +final logoutProvider = StateNotifierProvider( + (ref) => LogoutNotifier(ref: ref)); + +// 2. notifier +class LogoutNotifier extends StateNotifier { + final Ref ref; + LogoutNotifier({required this.ref}) : super(LogoutStateInit()); + void logout({ + required String M_UserID, + required String M_UserUsername, + }) async { + try { + state = LogoutStateLoading(); + final resp = await AuthRepository(dio: ref.read(dioProvider)) + .logout(M_UserID: M_UserID, M_UserUsername: M_UserUsername); + + // print(resp); + state = LogoutStateDone(message: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = LogoutStateError(message: e.message ?? ""); + } else { + state = LogoutStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class LogoutState extends Equatable { + final DateTime date; + const LogoutState(this.date); + @override + List get props => [date]; +} + +class LogoutStateInit extends LogoutState { + LogoutStateInit() : super(DateTime.now()); +} + +class LogoutStateLoading extends LogoutState { + LogoutStateLoading() : super(DateTime.now()); +} + +class LogoutStateError extends LogoutState { + final String message; + LogoutStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class LogoutStateDone extends LogoutState { + final String message; + LogoutStateDone({ + required this.message, + }) : super(DateTime.now()); +} diff --git a/lib/screen/loging/loging.dart b/lib/screen/loging/loging.dart new file mode 100644 index 0000000..b350405 --- /dev/null +++ b/lib/screen/loging/loging.dart @@ -0,0 +1,23 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; + +class LogingScreen extends HookConsumerWidget { + const LogingScreen({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final auth = ref.watch(authProvider); + return Material( + child: Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.all(20), + child: ListView( + children: [Text("a"), Text("AUTH"), Text(jsonEncode(auth))], + ), + ), + ); + } +} diff --git a/lib/screen/order/cancel_order_provider.dart b/lib/screen/order/cancel_order_provider.dart new file mode 100644 index 0000000..032aa6b --- /dev/null +++ b/lib/screen/order/cancel_order_provider.dart @@ -0,0 +1,65 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'package:mitra_corporate/repository/order_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class CancelOrderState extends Equatable { + final DateTime date; + const CancelOrderState(this.date); + @override + List get props => [date]; +} + +class CancelOrderStateInit extends CancelOrderState { + CancelOrderStateInit() : super(DateTime.now()); +} + +class CancelOrderStateLoading extends CancelOrderState { + CancelOrderStateLoading() : super(DateTime.now()); +} + +class CancelOrderStateError extends CancelOrderState { + final String message; + CancelOrderStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class CancelOrderStateDone extends CancelOrderState { + final String number; + CancelOrderStateDone({ + required this.number, + }) : super(DateTime.now()); +} + +//notifier +class CancelOrderNotifier extends StateNotifier { + final Ref ref; + CancelOrderNotifier({ + required this.ref, + }) : super(CancelOrderStateInit()); + + void cancel({required String token, required String orderID}) async { + try { + state = CancelOrderStateLoading(); + final dio = ref.read(dioProvider); + final resp = await OrderRepository(dio: dio) + .cancelOrder(token: token, orderID: orderID); + state = CancelOrderStateDone(number: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = CancelOrderStateError(message: e.message.toString()); + } else { + state = CancelOrderStateError(message: e.toString()); + } + } + } +} + +//provider +final CancelOrderProvider = + StateNotifierProvider( + (ref) => CancelOrderNotifier(ref: ref)); diff --git a/lib/screen/order/dialog_cancel_order.dart b/lib/screen/order/dialog_cancel_order.dart new file mode 100644 index 0000000..76479e2 --- /dev/null +++ b/lib/screen/order/dialog_cancel_order.dart @@ -0,0 +1,154 @@ +import 'package:flutter/material.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; + +class DialogCancelOrder extends StatelessWidget { + const DialogCancelOrder( + {super.key, + required this.orderNumber, + required this.cancel, + required this.orderID, + required this.loading, + required this.name}); + + final String name; + final String orderNumber; + final Function cancel; + final String orderID; + final bool loading; + + @override + Widget build(BuildContext context) { + return AlertDialog( + titlePadding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 24)), + actionsPadding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 24)), + contentPadding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 24)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8))), + title: Text( + 'Konfirmasi Pembatalan', + style: Constant.h4_600(context: context), + ), + content: Container( + constraints: BoxConstraints( + minHeight: Constant.getActualY(context: context, y: 100), + maxHeight: Constant.getActualY(context: context, y: 362), + ), + height: Constant.getActualY(context: context, y: 240), + child: Column( + children: [ + Text( + 'Anda yakin membatalkan order berikut ?', + style: Constant.body1_600(context: context), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 24), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 416), + child: Card( + color: Colors.white, + elevation: 1, + shape: RoundedRectangleBorder( + side: BorderSide(color: Constant.grey_200), + borderRadius: BorderRadius.circular(12)), + child: Container( + padding: EdgeInsets.symmetric( + horizontal: + Constant.getActualX(context: context, x: 24), + vertical: + Constant.getActualY(context: context, y: 24)), + child: Column( + children: [ + Row( + children: [ + Expanded( + flex: 5, + child: Text( + "Nomor Reg", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + ), + Expanded( + flex: 7, + child: Text( + orderNumber, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack), + ), + ) + ], + ), + SizedBox( + height: + Constant.getActualY(context: context, y: 12), + ), + Row( + children: [ + Expanded( + flex: 5, + child: Text( + "Nama", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + ), + Expanded( + flex: 7, + child: Text( + name, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack), + ), + ) + ], + ), + ], + )), + ), + ) + ], + )), + actions: [ + OutlinedButton( + style: OutlinedButton.styleFrom( + surfaceTintColor: Constant.primaryBlue, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: !loading ? () => Navigator.pop(context, 'Batal') : null, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: loading + ? LoadingAnimationWidget.staggeredDotsWave( + color: Constant.primaryBlue, size: 20) + : Text('Batal', + style: Constant.button_medium(context: context) + .copyWith(color: Constant.textBlack)), + ), + ), + ElevatedButton( + onPressed: !loading ? () => cancel(orderID) : null, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: loading + ? LoadingAnimationWidget.staggeredDotsWave( + color: Colors.white, size: 20) + : Text( + 'Yakin', + style: Constant.button_medium(context: context) + .copyWith(color: Colors.white), + ), + ), + ), + ], + ); + } +} diff --git a/lib/screen/order/dialog_edit.dart b/lib/screen/order/dialog_edit.dart new file mode 100644 index 0000000..48a897a --- /dev/null +++ b/lib/screen/order/dialog_edit.dart @@ -0,0 +1,141 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/app/constant.dart'; +import 'package:mitra_corporate/provider/order_provider.dart'; +import 'package:mitra_corporate/screen/order/edit_pemeriksaan.dart'; +import 'package:mitra_corporate/screen/order/form_detail_edit.dart'; + +import 'package:mitra_corporate/screen/registrasi/registrasi_sukses.dart'; +import 'package:mitra_corporate/widgets/stepper_edit.dart'; + +import 'form_edit_data_pasien.dart'; + +class DialogEdit extends HookConsumerWidget { + const DialogEdit({super.key}); + + // const DialogDataPasien({ + // Key? key, + // }) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final editStep = ref.watch(EditStepProvider); + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: SimpleDialog( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(12))), + contentPadding: const EdgeInsets.all(40.0), + children: [ + Column(children: [ + (editStep == 3) + ? Text('') + : SizedBox( + width: Constant.getActualX(context: context, x: 1080), + height: Constant.getActualY(context: context, y: 62), + child: Text('Edit Order', + style: Constant.h2_600(context: context) + .copyWith(color: Constant.textBlack)), + ), + SizedBox(height: Constant.getActualY(context: context, y: 40)), + //Stepper + (editStep == 3) + ? Text('') + : SizedBox( + width: Constant.getActualX(context: context, x: 1080), + height: Constant.getActualY(context: context, y: 100), + child: StepperEdit()), + SizedBox( + height: Constant.getActualY(context: context, y: 40), + ), + // membaca perubahan value dari registrasi_provider.dart + (editStep == 0) + ? FormEditDatapasien() + : (editStep == 1) + ? FormEditPemeriksaan() + : (editStep == 2) + ? DetailEdit() + : (editStep == 3) + ? RegistrasiSukses() + : Text('Tidak ada view'), + + // if(editStep == 1){ + // FormDataPasien() + // } else{ + // if(editStep == 2){ + // Text('2') + // }else{ + // Text('3') + // } + // } + // + // SizedBox(height: Constant.getActualY(context: context, y: 24)), + // Container( + // // height: Constant.getActualY(context: context, y: 100), + // // color: Colors.red, + // padding: const EdgeInsets.only(left: 32, right: 32), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // (editStep == 4) + // ? Text('') + // : OutlinedButton( + // style: OutlinedButton.styleFrom( + // backgroundColor: Colors.white, + // side: BorderSide( + // color: Constant.primaryBlue, width: 2), + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(8))), + // onPressed: () { + // ref + // .read(registrasiProvider.state) + // .update((state) => state - 1); + // }, + // child: SizedBox( + // height: Constant.getActualY(context: context, y: 56), + // child: Center( + // child: Text('Batal', + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textBlue)), + // ), + // )), + // SizedBox(width: Constant.getActualX(context: context, x: 24)), + // (editStep == 4) + // ? Text('') + // : ElevatedButton( + // style: ElevatedButton.styleFrom( + // backgroundColor: Constant.primaryBlue, + // side: + // BorderSide(color: Constant.primaryBlue, width: 2), + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(8), + // ), + // ), + + // // onPressed: () { + // // showDialog( + // // context: context, + // // builder: ((context) => DialogPemeriksaan())); + // // }, + // onPressed: () { + // ref + // .read(registrasiProvider.state) + // .update((state) => state + 1); + // }, + // child: SizedBox( + // height: Constant.getActualY(context: context, y: 56), + // child: Center( + // child: Text('Simpan Perubahan', + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textWhite)), + // ), + // )), + // ], + // ), + // ) + ]) + ]), + ); + } +} diff --git a/lib/screen/order/edit_order_provider.dart b/lib/screen/order/edit_order_provider.dart new file mode 100644 index 0000000..7ac1647 --- /dev/null +++ b/lib/screen/order/edit_order_provider.dart @@ -0,0 +1,64 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/registration_model.dart'; +import 'package:mitra_corporate/repository/order_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class EditOrderState extends Equatable { + final DateTime date; + const EditOrderState(this.date); + @override + List get props => [date]; +} + +class EditOrderStateInit extends EditOrderState { + EditOrderStateInit() : super(DateTime.now()); +} + +class EditOrderStateLoading extends EditOrderState { + EditOrderStateLoading() : super(DateTime.now()); +} + +class EditOrderStateError extends EditOrderState { + final String message; + EditOrderStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class EditOrderStateDone extends EditOrderState { + final String number; + EditOrderStateDone({ + required this.number, + }) : super(DateTime.now()); +} + +//notifier +class EditOrderNotifier extends StateNotifier { + final Ref ref; + EditOrderNotifier({ + required this.ref, + }) : super(EditOrderStateInit()); + + void editOrder({required RegistrationModel prm}) async { + try { + state = EditOrderStateLoading(); + final dio = ref.read(dioProvider); + final resp = await OrderRepository(dio: dio).editOrder(prm: prm); + state = EditOrderStateDone(number: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = EditOrderStateError(message: e.message.toString()); + } else { + state = EditOrderStateError(message: e.toString()); + } + } + } +} + +//provider +final EditOrderProvider = + StateNotifierProvider( + (ref) => EditOrderNotifier(ref: ref)); diff --git a/lib/screen/order/edit_pemeriksaan.dart b/lib/screen/order/edit_pemeriksaan.dart new file mode 100644 index 0000000..9681c47 --- /dev/null +++ b/lib/screen/order/edit_pemeriksaan.dart @@ -0,0 +1,655 @@ +import 'dart:convert'; +import 'dart:ui'; + +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/model/auth_model.dart'; +import 'package:mitra_corporate/model/registration_model.dart'; +import 'package:mitra_corporate/model/test_model.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/provider/order_provider.dart'; +import 'package:mitra_corporate/screen/registrasi/registrasi_prvider.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; +import 'package:scroll_to_index/scroll_to_index.dart'; + +import '../../app/constant.dart'; +import '../../provider/registrasi_provider.dart'; +import '../../widgets/custom_snackbar_widget.dart'; +import '../../widgets/custom_text_field.dart'; + +// ubah stateless jadi HookConsumerWidget +class FormEditPemeriksaan extends HookConsumerWidget { + const FormEditPemeriksaan({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final tempRegistrationData = ref.watch(EditDataProvider); + final selectedtest = ref.watch(selectedTestEditProvider); + final selectedPacket = ref.watch(selectedPacketEditProvider); + final selectedPacketOld = ref.watch(currentPacketProvider); + // inisialisasi baca provider idTipePeriksaStateProvider + final selectedTab = ref.watch(idTipePeriksaStateProvider); + final AuthModel auth = ref.watch(authProvider) ?? AuthModel(); + final listTest = useState>(List.empty()); + final testLoading = useState(false); + final searchCtr = useTextEditingController(text: ""); + final scrollCtr = useScrollController(); + final ctr = useState(AutoScrollController()); + + //get pemeriksaan from api + getPemeriksaan() { + ref + .read(GetFPPProvider.notifier) + .getData(mouID: auth.mUserMMouID ?? "0", token: auth.token ?? "0"); + } + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + getPemeriksaan(); + }); + return () {}; + }, []); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + int index = listTest.value + .indexWhere((element) => element.tabId == selectedTab); + if (searchCtr.text.isNotEmpty) { + ctr.value.scrollToIndex(index); + } + // scrollCtr.animateTo(double.parse(index.toString()), + // curve: Curves.linear, duration: Duration(milliseconds: 100)); + // // scrollCtr.jumpTo(double.parse(index.toString())); + }); + return () {}; + }, [selectedTab]); + + ref.listen( + GetFPPProvider, + (pref, next) { + if (next is GetFPPStateInit) { + testLoading.value = true; + } else if (next is GetFPPStateLoading) { + testLoading.value = true; + } else if (next is GetFPPStateError) { + testLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is GetFPPStateDone) { + listTest.value = next.model; + ref.read(tempTestListProvider.notifier).state = next.model; + + // tabController.length + + testLoading.value = false; + } + }, + ); + + // searchCtr.addListener(() { + // listTest.value.firstWhere((element) => element.items) + // },); + + return Material( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 500), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + suffixIcon: const Icon(EvaIcons.search), + controller: searchCtr, + isPassword: false, + hintText: "Cari Pemeriksaan", + labelText: "Cari Pemeriksaan", + ), + ), + SizedBox(height: Constant.getActualY(context: context, y: 12)), + // Tab + testLoading.value + ? LoadingAnimationWidget.discreteCircle( + color: Colors.blue, size: 20) + : ConstrainedBox( + constraints: BoxConstraints( + maxWidth: Constant.getActualX(context: context, x: 1080)), + child: SizedBox( + width: MediaQuery.of(context).size.width, + height: Constant.getActualY(context: context, y: 70), + child: ScrollConfiguration( + behavior: + ScrollConfiguration.of(context).copyWith(dragDevices: { + PointerDeviceKind.mouse, + PointerDeviceKind.touch, + PointerDeviceKind.trackpad, + PointerDeviceKind.stylus, + PointerDeviceKind.unknown + }), + child: ListView( + controller: ctr.value, + scrollDirection: Axis.horizontal, + children: listTest.value + .asMap() + .entries + .map((e) => AutoScrollTag( + key: ValueKey(e.key), + index: e.key, + controller: ctr.value, + child: Container( + decoration: BoxDecoration( + border: Border( + bottom: selectedTab == e.value.tabId + ? BorderSide( + width: 2, + color: Constant.primaryRed) + : const BorderSide( + color: Colors.transparent), + ), + ), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shadowColor: Constant.primaryRed + .withOpacity(0.5), + backgroundColor: Colors.transparent, + elevation: 0), + onPressed: () { + ref + .read(idTipePeriksaStateProvider + .state) + .update((state) => e.value.tabId!); + }, + child: Text( + e.value.tab!, + style: + Constant.body3_500(context: context) + .copyWith(color: Colors.black), + )), + ), + )) + .toList(), + ), + ), + ), + ), + + SizedBox(height: Constant.getActualY(context: context, y: 14)), + + // widget checkbox semua biar rapi + listCheckboxWidget( + search: searchCtr, + listTest: listTest, + loading: testLoading, + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + SizedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + OutlinedButton( + style: OutlinedButton.styleFrom( + backgroundColor: Colors.white, + side: BorderSide(color: Colors.grey.shade400, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: () { + // Navigator.pop(context); + ref + .read(EditStepProvider.state) + .update((state) => state - 1); + }, + child: SizedBox( + height: Constant.getActualY(context: context, y: 56), + child: Center( + child: Text('Sebelumnya', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack)), + ), + )), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Constant.green, + side: BorderSide(color: Constant.green, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + onPressed: () { + if (selectedtest.isEmpty && selectedPacket.isEmpty) { + SanckbarWidget(context, "Belum memilih Pemeriksaan", + snackbarType.warning); + return; + } + List ts = List.empty(growable: true); + double total = 0; + for (var i = 0; i < selectedtest.length; i++) { + var splitted = selectedtest[i].split("|"); + var tabid = splitted[0]; + var testid = splitted[1]; + for (var j = 0; j < listTest.value.length; j++) { + if (listTest.value[j].tabId == int.parse(tabid)) { + var tests = listTest.value[j].items; + for (var k = 0; k < tests!.length; k++) { + if (tests[k].testID == testid) { + ts.add(Tests( + date: DateTime.now().toString(), + id: tests[k].testID, + name: tests[k].testName, + price: tests[k].testPrice, + sasCode: tests[k].sasCode, + // specimen: "", + tab: tabid, + )); + total = total + + double.parse(tests[k].testPrice ?? "0"); + } + } + } + } + } + List pkt = List.empty(growable: true); + for (var i = 0; i < selectedPacket.length; i++) { + var paket = listTest.value + .firstWhere((element) => element.isPaket == "Y"); + paket.items?.forEach((element) { + if (element.testID == selectedPacket[i]) { + if (selectedPacketOld.isNotEmpty) { + for (var i in selectedPacketOld) { + var splitted = i.split('|'); + if (splitted[1] == element.testID) { + pkt.add(Paket( + detailID: splitted[0], + id: element.testID, + arrTest: element.arrTest, + detail: element.sasCode, + name: element.testName)); + total = total + + double.parse(element.testPrice ?? "0"); + } else { + pkt.add(Paket( + detailID: "new", + id: element.testID, + price: element.testPrice, + type: element.type, + arrTest: element.arrTest, + detail: element.sasCode, + name: element.testName)); + total = total + + double.parse(element.testPrice ?? "0"); + } + } + } else { + pkt.add(Paket( + detailID: "new", + id: element.testID, + arrTest: element.arrTest, + detail: element.sasCode, + price: element.testPrice, + type: element.type, + name: element.testName)); + total = + total + double.parse(element.testPrice ?? "0"); + } + } + }); + } + bool paketValidation = false; + bool testValidation = false; + for (var e in pkt) { + var test = e.arrTest?.split(","); + test?.forEach((f) { + for (var g in pkt) { + if (e.id?.trim() != g.id?.trim()) { + var testCek = g.arrTest!.split(","); + var cek = testCek.firstWhere( + (h) => h.trim() == f.trim(), + orElse: () => "-1", + ); + if (cek != "-1") { + paketValidation = true; + } + } + } + }); + } + for (var e in pkt) { + var test = e.arrTest?.split(","); + test?.forEach((f) { + for (var g in ts) { + if (f.trim() == g.id?.trim()) { + testValidation = true; + } + } + }); + } + if (paketValidation) { + SanckbarWidget( + context, + "Paket pilihan memiliki pemeriksaan yang sama ", + snackbarType.warning); + return; + } + if (testValidation) { + SanckbarWidget( + context, + "Paket pilihan memiliki pemeriksaan yang sama dengan test pilihan", + snackbarType.warning); + return; + } + ref.read(EditDataProvider.notifier).state = + RegistrationModel( + orderID: tempRegistrationData.orderID, + patientId: tempRegistrationData.patientId, + token: auth.token, + patientData: tempRegistrationData.patientData, + specimens: tempRegistrationData.specimens, + bahan: tempRegistrationData.bahan, + paket: pkt, + tests: ts, + total: total.toString()); + print("Specimen awal"); + print(jsonEncode(tempRegistrationData.specimens)); + print("Bahan awal"); + print(jsonEncode(tempRegistrationData.bahan)); + ref.read(EditStepProvider.state).update((state) => 2); + }, + child: SizedBox( + height: Constant.getActualY(context: context, y: 56), + child: Center( + child: Text('Berikutnya', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textWhite)), + ), + )), + ], + ), + ) + ], + )); + } +} + +class listCheckboxWidget extends HookConsumerWidget { + const listCheckboxWidget( + {super.key, + required this.listTest, + required this.loading, + required this.search}); + final ValueNotifier> listTest; + final ValueNotifier loading; + final TextEditingController search; + + @override + Widget build(BuildContext context, WidgetRef ref) { + bool? isChecked = false; + final tests = useState>(List.empty()); + final isPaket = useState(false); + + // inisialisasi state provider buat baca idTipePeriksa yang di klik + final selectedTab = ref.watch(idTipePeriksaStateProvider); + final selectedTest = ref.watch(selectedTestEditProvider); + final selectedPacket = ref.watch(selectedPacketEditProvider); + final scrollCtr = useScrollController(); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + if (loading.value == false && listTest.value.isNotEmpty) { + // TestModel temp = listTest.value[0]; + var selected = listTest.value + .firstWhere((element) => element.tabId == selectedTab); + if (selected.isPaket == 'Y') { + isPaket.value = true; + } else { + isPaket.value = false; + } + if (search.text.isNotEmpty) { + List filter = selected.items! + .where((element) => + element.testName!.toLowerCase().contains(search.text)) + .toList(); + tests.value = filter; + } else { + tests.value = selected.items ?? []; + } + } + }); + return () {}; + }, [selectedTab, loading.value]); + + search.addListener( + () { + if (loading.value == false && listTest.value.isNotEmpty) { + for (var i = 0; i < listTest.value.length; i++) { + List flt = listTest.value[i].items! + .where((element) => + element.testName!.toLowerCase().contains(search.text)) + .toList(); + if (flt.isNotEmpty) { + ref.read(idTipePeriksaStateProvider.notifier).state = + listTest.value[i].tabId!; + break; + } + } + + var selected = listTest.value + .firstWhere((element) => element.tabId == selectedTab); + + if (search.text.isNotEmpty) { + List filter = selected.items! + .where((element) => + element.testName!.toLowerCase().contains(search.text)) + .toList(); + tests.value = filter; + } else { + tests.value = selected.items ?? []; + } + } + }, + ); + + selectedItem(int idTab, String idTest) { + List temp = selectedTest.toSet().toList(growable: true); + var data = "$idTab|$idTest"; + var cek = temp.firstWhere( + (element) => element == data, + orElse: () => "-1", + ); + if (cek == "-1") { + temp.add(data); + } else { + temp.remove(cek); + } + ref.read(selectedTestEditProvider.notifier).state = temp.toSet().toList(); + } + + valueCek(int idTab, String idTest) { + var data = "$idTab|$idTest"; + var cek = selectedTest.firstWhere( + (element) => element == data, + orElse: () => "-1", + ); + if (cek == "-1") { + return false; + } else { + return true; + } + } + + selectPacket(String idTest) { + List temp = selectedPacket.toSet().toList(growable: true); + + var cek = temp.firstWhere( + (element) => element == idTest, + orElse: () => "-1", + ); + if (cek == "-1") { + temp.add(idTest); + } else { + temp.remove(cek); + } + ref.read(selectedPacketEditProvider.notifier).state = + temp.toSet().toList(); + } + + valueCekPacket(String idTest) { + var cek = selectedPacket.firstWhere( + (element) => element == idTest, + orElse: () => "-1", + ); + if (cek == "-1") { + return false; + } else { + return true; + } + } + + // buat data2 dari List Periksa + + return Row(children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 1080), + height: Constant.getActualY(context: context, y: 230), + child: loading.value + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 30), + ) + : Scrollbar( + controller: scrollCtr, + thumbVisibility: true, + trackVisibility: true, + child: ScrollConfiguration( + behavior: + ScrollConfiguration.of(context).copyWith(dragDevices: { + PointerDeviceKind.mouse, + PointerDeviceKind.touch, + PointerDeviceKind.trackpad, + PointerDeviceKind.stylus, + PointerDeviceKind.unknown + }), + child: isPaket.value + ? ListView( + controller: scrollCtr, + children: tests.value + .map( + (e) => Container( + margin: EdgeInsets.symmetric(vertical: 5), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + children: [ + Checkbox( + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(4)), + // value: valueCek( + // selectedTab, e.testID!), + value: valueCekPacket(e.testID!), + onChanged: (value) { + print(value); + selectPacket(e.testID!); + // selectedItem( + // selectedTab, e.testID!); + }), + SizedBox( + // constraints: BoxConstraints( + // maxWidth: + // ), + // color: Colors.red, + width: Constant.getActualX( + context: context, x: 380), + child: Text('${e.testName}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Constant.body3_400( + context: context) + .copyWith( + color: Constant + .textBlack)), + ), + ], + ), + Padding( + padding: EdgeInsets.only(left: 30), + child: Text( + e.sasCode ?? "", + style: Constant.caption2_400( + context: context) + .copyWith( + color: Constant.textGrey), + ), + ) + ], + ), + ), + ) + .toList(), + ) + : GridView.count( + controller: scrollCtr, + shrinkWrap: true, + crossAxisCount: 2, + childAspectRatio: 15, + children: tests.value + .map( + (e) => Container( + // color: Colors.primaries[ + // Random().nextInt(Colors.primaries.length)], + child: Row( + children: [ + Checkbox( + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(4)), + // value: valueCek(selectedTab, e.testID!), + value: + valueCek(selectedTab, e.testID!), + onChanged: (value) { + print(value); + selectedItem( + selectedTab, e.testID!); + }), + SizedBox( + // constraints: BoxConstraints( + // maxWidth: + // ), + // color: Colors.red, + width: Constant.getActualX( + context: context, x: 380), + child: Text('${e.testName}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Constant.body3_400( + context: context) + .copyWith( + color: Constant.textBlack)), + ), + // Container( + // // constraints: BoxConstraints( + // // maxWidth: + // // ), + // // color: Colors.red, + // // width: + // // Constant.getActualX(context: context, x: 350), + // child: Text( + // Constant.convertToIdr( + // int.parse(e.testPrice ?? "0"), 0), + // style: Constant.body3_500( + // context: context) + // .copyWith(color: Constant.textBlack)), + // ), + ], + ), + ), + ) + .toList(), + ), + ), + ), + ), + ]); + } +} diff --git a/lib/screen/order/form_detail_edit.dart b/lib/screen/order/form_detail_edit.dart new file mode 100644 index 0000000..1d73a50 --- /dev/null +++ b/lib/screen/order/form_detail_edit.dart @@ -0,0 +1,663 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; + +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/intl.dart'; + +import 'package:mitra_corporate/model/registration_model.dart'; +import 'package:mitra_corporate/model/test_model.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/provider/order_provider.dart'; +import 'package:mitra_corporate/provider/registrasi_provider.dart'; +import 'package:mitra_corporate/screen/order/edit_order_provider.dart'; +import 'package:mitra_corporate/screen/registrasi/get_specimen_provider.dart'; +import 'package:mitra_corporate/widgets/tests_tabel_edit_widget.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../widgets/custom_snackbar_widget.dart'; + +class DetailEdit extends HookConsumerWidget { + const DetailEdit({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final auth = ref.watch(authProvider); + final tempRegistrationData = ref.watch(EditDataProvider); + final selectedtestDataOld = ref.watch(selectedtestData); + final selectedPacketOld = ref.watch(currentPacketProvider); + final testListAll = ref.read(tempTestListProvider); + final selectedTest = ref.read(selectedTestEditProvider); + final dialogAction = ref.watch(dialogOrderActionProvider); + + final testList = useState>(List.empty()); + final testScrollCtr = useScrollController(); + final specimenLoading = useState(false); + final addOrderLoading = ref.watch(editOrderLoadingProvider); + final tabctr = useTabController(initialLength: 2); + final selectedTab = useState(0); + + //get specimen & bahan from list of tests + getSpecimen() { + List> test = List.empty(growable: true); + tempRegistrationData.tests?.forEach((element) { + test.add({ + "id": element.id ?? "0", + "tab": element.tab ?? "0", + "sasCode": element.sasCode ?? "-" + }); + }); + // print(jsonEncode(test)); + ref + .read(GetSpecimenProvider.notifier) + .getData(arrTest: test, token: auth?.token ?? "0"); + } + + ref.listen( + GetSpecimenProvider, + (pref, next) { + if (next is GetSpecimenStateInit) { + specimenLoading.value = true; + } else if (next is GetSpecimenStateLoading) { + specimenLoading.value = true; + } else if (next is GetSpecimenStateError) { + specimenLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is GetSpecimenStateDone) { + var tests = tempRegistrationData.tests; + //assign specimn & bahan + List nw = next.model; + List sp = List.empty(growable: true); + List bhn = List.empty(growable: true); + + for (var i = 0; i < tests!.length; i++) { + var tes = tests[i]; + var cek = "${tes.tab}|${tes.id}"; + for (var j = 0; j < nw.length; j++) { + var cek2 = "${nw[j].tab}|${nw[j].id}"; + if (cek == cek2) { + tests[i].specimen = nw[j].specimen?.toSet().toList(); + tests[i].bahan = nw[j].bahan?.toSet().toList(); + tests[i].detailID = 'new'; + + nw[j].specimen?.forEach((element) { + var a = sp.firstWhere( + (dt) => dt.id == element.id, + orElse: () => + Specimens(ctr: TextEditingController(), id: "new"), + ); + if (a.id == "new") { + element.detailID = "new"; + sp.add(element); + } + }); + nw[j].bahan?.forEach((element) { + var a = bhn.firstWhere( + (dt) => dt.id == element.id, + orElse: () => + Bahan(ctr: TextEditingController(), id: "new"), + ); + if (a.id == "new") { + element.detailID = "new"; + bhn.add(element); + } + }); + } + } + } + print("SPECIMEN"); + print(jsonEncode(sp)); + print("BAHAN"); + print(jsonEncode(bhn)); + for (var e in sp) { + tempRegistrationData.specimens?.forEach((i) { + if (e.id == i.id) { + e.detailID = i.detailID; + e.ctr.text = i.amount ?? "0"; + e.amount = i.amount ?? "0"; + } + }); + } + for (var e in bhn) { + tempRegistrationData.bahan?.forEach((i) { + if (e.id == i.id) { + e.detailID = i.detailID; + e.ctr.text = i.amount ?? "0"; + e.amount = i.amount ?? "0"; + } + }); + } + print("SPECIMEN2"); + print(jsonEncode(sp)); + print("BAHAN2"); + print(jsonEncode(bhn)); + for (var e in tests) { + for (var i in selectedtestDataOld) { + //detailid|idtest|testdate + // 68|205|2023-10-18 14:32:19 + var splitted = i.split('|'); + if (e.id == splitted[1]) { + e.detailID = splitted[0]; + + ///assign date + // e.date = splitted[2]; + e.date = DateFormat('dd-MM-yyyy HH:mm') + .format(DateTime.parse(splitted[2])) + .toString(); + } + } + } + ref.read(EditDataProvider.notifier).state = RegistrationModel( + orderID: tempRegistrationData.orderID, + patientId: tempRegistrationData.patientId, + token: auth?.token, + patientData: tempRegistrationData.patientData, + paket: tempRegistrationData.paket, + tests: tests, + specimens: sp.toSet().toList(), + bahan: bhn.toSet().toList(), + total: tempRegistrationData.total); + specimenLoading.value = false; + } + }, + ); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + List temp = List.empty(growable: true); + for (var element in testListAll) { + // "$idTab|$idTest" + for (var i in element.items!) { + String cek = "${element.tabId}|${i.testID}"; + String ck = selectedTest.firstWhere( + (j) => j == cek, + orElse: () => "-1", + ); + if (ck != "-1") { + temp.add(i); + } + } + } + + testList.value = temp; + + getSpecimen(); + }); + return () {}; + }, []); + + //delete tests & auto cek & delete specimen/bahan + delete(String idTest, String idtab) { + List temp = tempRegistrationData.tests ?? []; + List selectedTest = ref.watch(selectedTestEditProvider); + String cek = "$idtab|$idTest"; + double total = double.parse(tempRegistrationData.total!); + + List oldSp = tempRegistrationData.specimens ?? []; + List oldBhn = tempRegistrationData.bahan ?? []; + + Tests removed = tempRegistrationData.tests!.firstWhere( + (element) => element.id == idTest && element.tab == idtab); + temp.removeWhere( + (element) => element.id == idTest && element.tab == idtab); + selectedTest.removeWhere((element) => element == cek); + total = total - double.parse(removed.price!); + // var tests = tempRegistrationData.tests; + + // List nw = next.model; + List newSp = List.empty(growable: true); + List newBhn = List.empty(growable: true); + + for (var j = 0; j < temp.length; j++) { + temp[j].specimen?.forEach((element) { + var a = newSp.firstWhere( + (dt) => dt.id == element.id, + orElse: () => Specimens(ctr: TextEditingController(), id: "new"), + ); + if (a.id == "new") { + newSp.add(element); + } + }); + temp[j].bahan?.forEach((element) { + var a = newBhn.firstWhere( + (dt) => dt.id == element.id, + orElse: () => Bahan(ctr: TextEditingController(), id: "new"), + ); + if (a.id == "new") { + newBhn.add(element); + } + }); + } + // print("old sp"); + // print(jsonEncode(oldSp)); + // print("new sp"); + // print(jsonEncode(newSp)); + //compare old dp and new sp to get deleted specimen + List deletedSp = List.empty(growable: true); + for (var i = 0; i < oldSp.length; i++) { + Specimens sp = newSp.firstWhere((element) => element.id == oldSp[i].id, + orElse: () => + Specimens(ctr: TextEditingController(), id: "deleted")); + if (sp.id == "deleted") { + deletedSp.add(oldSp[i]); + } + } + List tempSp = tempRegistrationData.specimens ?? []; + for (var e in deletedSp) { + tempSp.removeWhere((element) => element.id == e.id); + } + // print("deleted sp"); + // print(jsonEncode(deletedSp)); + // print("old bhn"); + // print(jsonEncode(oldBhn)); + // print("new bhn"); + // print(jsonEncode(newBhn)); + //compare old bhn and new bhn to get deleted bahan + List deletedBhn = List.empty(growable: true); + for (var i = 0; i < oldBhn.length; i++) { + Bahan sp = newBhn.firstWhere((element) => element.id == oldBhn[i].id, + orElse: () => Bahan(ctr: TextEditingController(), id: "deleted")); + if (sp.id == "deleted") { + deletedBhn.add(oldBhn[i]); + } + } + List tempBhn = tempRegistrationData.bahan ?? []; + for (var e in deletedBhn) { + tempBhn.removeWhere((element) => element.id == e.id); + } + // print("deleted bahan"); + // print(jsonEncode(deletedBhn)); + + ref.read(EditDataProvider.notifier).state = RegistrationModel( + orderID: tempRegistrationData.orderID, + patientId: tempRegistrationData.patientId, + token: auth?.token, + patientData: tempRegistrationData.patientData, + specimens: tempSp, + paket: tempRegistrationData.paket, + bahan: tempBhn, + tests: temp, + total: total.toString()); + ref.read(selectedTestEditProvider.notifier).state = selectedTest; + if (selectedTest.isEmpty) { + ref.read(EditStepProvider.state).update((state) => state - 1); + } + } + + deletePaket(String idPaket) { + List temp = tempRegistrationData.paket ?? []; + List selectedPaket = ref.watch(selectedPacketEditProvider); + + selectedPaket.removeWhere((element) => element == idPaket); + temp.removeWhere((element) => element.id == idPaket); + + ref.read(selectedPacketEditProvider.notifier).state = selectedPaket; + ref.read(EditDataProvider.notifier).state = RegistrationModel( + orderID: tempRegistrationData.orderID, + patientId: tempRegistrationData.patientId, + token: auth?.token, + patientData: tempRegistrationData.patientData, + specimens: tempRegistrationData.specimens, + paket: temp, + bahan: tempRegistrationData.bahan, + tests: tempRegistrationData.tests, + total: tempRegistrationData.total); + } + + Future pickDate(DateTime initialdate) => showDatePicker( + // locale: Locale("id", "ID"), + context: context, + initialEntryMode: DatePickerEntryMode.calendarOnly, + cancelText: "Batal", + confirmText: "Simpan", + helpText: "Pilih Tanggal", + initialDate: initialdate, + firstDate: DateTime(1800), + lastDate: DateTime(2100)); + + Future pickTime(TimeOfDay initialTime) => showTimePicker( + context: context, + initialTime: initialTime, + cancelText: "Batal", + confirmText: "Simpan", + helpText: "Masukkan Jam dan Menit", + hourLabelText: "Jam", + minuteLabelText: "Menit", + builder: (context, childWidget) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + // Using 24-Hour format + alwaysUse24HourFormat: true), + // If you want 12-Hour format, just change alwaysUse24HourFormat to false or remove all the builder argument + child: childWidget!); + }, + initialEntryMode: TimePickerEntryMode.input); + + Future pickDateTime(String idtab, String idtest, String dt) async { + // DateTime selectedDate = DateTime.parse(dt); + // DateTime? date = await pickDate(selectedDate); + // if (date == null) return; + + // TimeOfDay? time = await pickTime( + // TimeOfDay(hour: selectedDate.hour, minute: selectedDate.minute)); + // if (time == null) return; + // var test = tempRegistrationData.tests ?? []; + // for (var i = 0; i < test.length; i++) { + // if (test[i].id == idtest && test[i].tab == idtab) { + // test[i].date = + // DateTime(date.year, date.month, date.day, time.hour, time.minute) + // .toString(); + // } + // } + var test = tempRegistrationData.tests ?? []; + for (var i = 0; i < test.length; i++) { + if (test[i].id == idtest && test[i].tab == idtab) { + test[i].date = dt; + } + } + ref.read(EditDataProvider.notifier).state = RegistrationModel( + orderID: tempRegistrationData.orderID, + patientData: tempRegistrationData.patientData, + specimens: tempRegistrationData.specimens, + tests: test, + token: auth?.token, + patientId: tempRegistrationData.patientId, + bahan: tempRegistrationData.bahan, + total: tempRegistrationData.total); + } + + bool validateDate(String? value) { + if (value == null || value.isEmpty) { + SanckbarWidget(context, "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + final splitted = value.split(" "); + if (splitted.length == 2) { + final date = splitted[0]; + final time = splitted[1]; + final splittedDate = date.split("-"); + final splittedTime = time.split(":"); + if (splittedDate.length == 3) { + final day = int.tryParse(splittedDate[0]); + final month = int.tryParse(splittedDate[1]); + final year = int.tryParse(splittedDate[2]); + if (day != null) { + if (day > 31) { + SanckbarWidget( + context, + "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + } + if (month != null) { + if (month > 12) { + SanckbarWidget( + context, + "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + } + // if (year == null) { + // return "Format salah"; + // } + } else { + SanckbarWidget(context, "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + if (splittedTime.length == 2) { + var hour = int.tryParse(splittedTime[0]); + var sec = int.tryParse(splittedTime[1]); + if (hour != null) { + if (hour > 24) { + SanckbarWidget( + context, + "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + } + if (sec != null) { + if (sec > 60) { + SanckbarWidget( + context, + "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + } + } else { + SanckbarWidget(context, "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + } else { + SanckbarWidget(context, "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + return false; + } + + editOrder() { + print(jsonEncode(tempRegistrationData)); + // tempRegistrationData.tests!.forEach((element) { + // if (element.date == null || element.date!.isEmpty) { + // SanckbarWidget( + // context, + // "Waktu pengambilan sample ${element.name} Tidak boleh kosong", + // snackbarType.warning); + // return; + // } + // if (validateDate(element.date)) { + // return; + // } + + // var coba = Jiffy.parse(element.date!, pattern: 'dd-MM-yyyy HH:mm') + // .format(pattern: 'yyyy-MM-dd HH:mm'); + // }); + // print(jsonEncode(tempRegistrationData)); + // return; + ref.read(EditOrderProvider.notifier).editOrder(prm: tempRegistrationData); + } + + ref.listen( + EditOrderProvider, + (pref, next) { + if (next is EditOrderStateInit) { + ref.read(editOrderLoadingProvider.notifier).state = true; + } else if (next is EditOrderStateLoading) { + ref.read(editOrderLoadingProvider.notifier).state = true; + } else if (next is EditOrderStateError) { + ref.read(editOrderLoadingProvider.notifier).state = false; + + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is EditOrderStateDone) { + ref.read(orderNumberProvider.notifier).state = next.number; + print(next.number); + SanckbarWidget(context, "Edit Order Berhasil ", snackbarType.success); + + ref.read(editOrderLoadingProvider.notifier).state = false; + + Navigator.pop(context); + ref.read(EditStepProvider.notifier).state = 0; + ref.read(tabPeriksaprovider.notifier).state = 1; + ref.read(EditDataProvider.notifier).state = RegistrationModel(); + ref.read(selectedtestData.notifier).state = + List.empty(growable: true); + ref.read(tempTestListProvider.notifier).state = + List.empty(growable: true); + ref.read(currentPacketProvider.notifier).state = + List.empty(growable: true); + ref.read(selectedPacketEditProvider.notifier).state = List.empty(); + } + }, + ); + + return Material( + child: Container( + width: Constant.getActualX(context: context, x: 1160), + // height: MediaQuery.of(context).size.height, + color: Colors.white, + child: Column( + children: [ + Row( + children: [ + SizedBox( + // color: Colors.red, + width: Constant.getActualX(context: context, x: 1160), + child: TestsTableEditWidget( + paketDelete: deletePaket, + specimenLoading: specimenLoading, + testScrollCtr: testScrollCtr, + tempRegistrationData: tempRegistrationData, + addOrderLoading: addOrderLoading, + delete: delete, + pickDateTime: pickDateTime) + // TestsTableWEditWidget( + // specimenLoading: specimenLoading, + // testScrollCtr: testScrollCtr, + // tempRegistrationData: tempRegistrationData, + // addOrderLoading: addOrderLoading, + // delete: delete, + // pickDateTime: pickDateTime), + ), + // Container( + // width: + // Constant.getActualX(context: context, x: 1160) * 0.05, + // ), + // Container( + // // color: Colors.red, + // width: + // Constant.getActualX(context: context, x: 1160) * 0.24, + // child: Column( + // children: [ + // Container( + // width: + // Constant.getActualX(context: context, x: 1160) * + // 0.24, + // child: TabBar( + // controller: tabctr, + // labelColor: Colors.black, + // labelStyle: Constant.body3_500(context: context) + // .copyWith(color: Colors.black), + // indicatorColor: Constant.primaryRed, + // onTap: (value) => selectedTab.value = value, + // tabs: [ + // Tab( + // text: "Specimen", + // ), + // Tab( + // text: "Bahan", + // ) + // ]), + // ), + // SizedBox( + // height: + // Constant.getActualY(context: context, y: 20), + // ), + // if (selectedTab.value == 0) + // SpecimenTableWidget( + // specimenLoading: specimenLoading, + // tempRegistrationData: tempRegistrationData, + // addOrderLoading: addOrderLoading), + // if (selectedTab.value == 1) + // BahanTableWidget( + // specimenLoading: specimenLoading, + // tempRegistrationData: tempRegistrationData, + // addOrderLoading: addOrderLoading) + + // // SizedBox( + // // height: + // // Constant.getActualY(context: context, y: 20), + // // ), + // // BahanTableWidget( + // // specimenLoading: specimenLoading, + // // tempRegistrationData: tempRegistrationData, + // // addOrderLoading: addOrderLoading), + // ], + // ), + // ), + ], + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + OutlinedButton( + style: OutlinedButton.styleFrom( + backgroundColor: Colors.white, + side: BorderSide( + color: Colors.grey.shade400, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: addOrderLoading == false + ? () { + // Navigator.pop(context); + ref + .read(EditStepProvider.state) + .update((state) => state - 1); + } + : null, + child: SizedBox( + height: + Constant.getActualY(context: context, y: 56), + child: Center( + child: addOrderLoading + ? LoadingAnimationWidget.staggeredDotsWave( + color: Constant.primaryRed, size: 30) + : Text('Sebelumnya', + style: Constant.body3_600( + context: context) + .copyWith(color: Constant.textBlack)), + ), + )), + SizedBox( + width: Constant.getActualX(context: context, x: 24)), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Constant.green, + side: BorderSide(color: Constant.green, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + + // onPressed: () { + // showDialog( + // context: context, + // builder: ((context) => DialogPemeriksaan())); + // }, + onPressed: addOrderLoading == false + ? () { + editOrder(); + } + : null, + child: SizedBox( + height: + Constant.getActualY(context: context, y: 56), + child: Center( + child: addOrderLoading + ? LoadingAnimationWidget.staggeredDotsWave( + color: Colors.white, size: 30) + : Text('Simpan', + style: Constant.body3_600( + context: context) + .copyWith(color: Constant.textWhite)), + ), + )), + ], + ), + ) + ], + ))); + } +} diff --git a/lib/screen/order/form_edit_data_pasien.dart b/lib/screen/order/form_edit_data_pasien.dart new file mode 100644 index 0000000..4efdabe --- /dev/null +++ b/lib/screen/order/form_edit_data_pasien.dart @@ -0,0 +1,896 @@ +import 'package:age_calculator/age_calculator.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/intl.dart'; +import 'package:jiffy/jiffy.dart'; +import 'package:mitra_corporate/app/constant.dart'; +import 'package:mitra_corporate/model/registration_model.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/provider/order_provider.dart'; +import 'package:mitra_corporate/screen/registrasi/get_filter_provider.dart'; +import 'package:mitra_corporate/widgets/custom_snackbar_widget.dart'; +import 'package:mitra_corporate/widgets/custom_text_field.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +class FormEditDatapasien extends HookConsumerWidget { + const FormEditDatapasien({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final tempPatient = ref.watch(EditDataProvider); + final auth = ref.watch(authProvider); + //withoutnik apabila tanpa nik maka true + final withoutNIK = useState(false); + final getFilterLoading = useState(false); + final sapaanKey = useState(1); + final genderKey = useState(1000); + final prefixCtr = useTextEditingController(text: ""); + final nameCtr = useTextEditingController(text: ""); + final suffixCtr = useTextEditingController(text: ""); + final nikCtr = useTextEditingController(text: ""); + final nipCtr = useTextEditingController(text: ""); + final hpCtr = useTextEditingController(text: ""); + final noRmCtr = useTextEditingController(text: ""); + final addressCtr = useTextEditingController(text: ""); + final diagnosisCtr = useTextEditingController(text: ""); + final noteCtr = useTextEditingController(text: ""); + final jabatanCtr = useTextEditingController(text: ""); + final kedudukanCtr = useTextEditingController(text: ""); + final lokasiCtr = useTextEditingController(text: ""); + final pekerjaanCtr = useTextEditingController(text: ""); + final sapaan = useState>(List.empty()); + final selectedSaluation = useState(CustomDropDownModel()); + final gender = useState>(List.empty()); + final selectedGender = useState(CustomDropDownModel()); + + //select salah satu sapaan + onSelectSaluation(CustomDropDownModel value) { + selectedSaluation.value = value; + selectedGender.value = + gender.value.firstWhere((element) => element.id == value.type); + + genderKey.value = genderKey.value + 1; + // sapaanKey.value = sapaanKey.value + 1; + } + + //update gender saat sapaan dipilih + selectedSaluation.addListener(() { + selectedGender.value = gender.value + .firstWhere((element) => element.id == selectedSaluation.value.type); + genderKey.value = genderKey.value + 1; + }); + + //select gender + onSelectGender(CustomDropDownModel value) { + selectedGender.value = value; + } + + final dobRb = useState('date'); + final dobCtr = useTextEditingController( + text: DateFormat('dd-MM-yyyy').format(DateTime.now())); + final dobState = useState(DateTime.now()); + + final yearCtr = useTextEditingController(text: "0"); + final monthCtr = useTextEditingController(text: "0"); + final dayCtr = useTextEditingController(text: "0"); + + //perhitungan umur dari tanggal ke angka + DateDuration calculateAge(DateTime birthDate) { + DateTime currentDate = DateTime.now(); + var age = AgeCalculator.age(birthDate, today: currentDate); + return age; + } + + dobState.addListener( + () { + // print(calculateAge(dobState.value)); + if (dobRb.value == "date") { + var age = calculateAge(dobState.value); + yearCtr.text = age.years.toString(); + monthCtr.text = age.months.toString(); + dayCtr.text = age.days.toString(); + } + }, + ); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + nameCtr.text = tempPatient.patientData?.name ?? ""; + prefixCtr.text = tempPatient.patientData?.prefix ?? ""; + suffixCtr.text = tempPatient.patientData?.suffix ?? ""; + nikCtr.text = tempPatient.patientData?.nik ?? ""; + nipCtr.text = tempPatient.patientData?.nip ?? ""; + withoutNIK.value = + tempPatient.patientData?.withoutNIK == 'Y' ? true : false; + hpCtr.text = tempPatient.patientData?.hp ?? ""; + addressCtr.text = tempPatient.patientData?.address ?? ""; + diagnosisCtr.text = tempPatient.patientData?.diagnosis ?? ""; + noteCtr.text = tempPatient.patientData?.note ?? ""; + noRmCtr.text = tempPatient.patientData?.noRM ?? ""; + jabatanCtr.text = tempPatient.patientData?.jabatan ?? ""; + kedudukanCtr.text = tempPatient.patientData?.kedudukan ?? ""; + lokasiCtr.text = tempPatient.patientData?.lokasi ?? ""; + pekerjaanCtr.text = tempPatient.patientData?.pekerjaan ?? ""; + // DateFormat('dd-MM-yyyy').format(DateTime.now()) + dobCtr.text = DateFormat('dd-MM-yyyy').format(DateTime.parse( + tempPatient.patientData?.dob ?? DateTime.now().toString())); + dobState.value = DateTime.parse( + tempPatient.patientData?.dob ?? DateTime.now().toString()); + ref + .read(GetFilterRegistrationProvider.notifier) + .getData(token: auth!.token ?? ""); + }); + return () {}; + }, []); + + ref.listen( + GetFilterRegistrationProvider, + (previous, next) { + if (next is GetFilterRegistrationStateInit) { + getFilterLoading.value = true; + } else if (next is GetFilterRegistrationStateLoading) { + getFilterLoading.value = true; + } else if (next is GetFilterRegistrationStateError) { + SanckbarWidget(context, next.message, snackbarType.error); + getFilterLoading.value = false; + Constant.autoLogout(context: context, msg: next.message); + } else if (next is GetFilterRegistrationStateDone) { + // print(jsonEncode(next.model)); + sapaan.value = next.model.titles!; + gender.value = next.model.gender!; + // selectedGender.value = next.model.gender![0]; + if (tempPatient.patientData?.saluation != null) { + var sp = next.model.titles!.firstWhere( + (element) => element.id == tempPatient.patientData?.saluation); + selectedSaluation.value = sp; + } + if (tempPatient.patientData?.gender != null) { + var gd = next.model.gender!.firstWhere( + (element) => element.id == tempPatient.patientData?.gender); + selectedGender.value = gd; + } + sapaanKey.value = sapaanKey.value + 1; + genderKey.value = genderKey.value + 1; + getFilterLoading.value = false; + } + }, + ); + + //Function ke step selanjutny(pilih pemeriksaan) + nextStep() { + if (withoutNIK.value == false) { + if (nikCtr.text.isEmpty) { + SanckbarWidget(context, "NIK Harus diisi", snackbarType.warning); + return; + } + if (nikCtr.text.length < 16 || nikCtr.text.length > 16) { + SanckbarWidget( + context, "NIK berjumlah 16 digit", snackbarType.warning); + return; + } + } + + if (nameCtr.text.isEmpty || selectedGender.value.id == null) { + // print(nameCtr.text); + // print(selectedGender.value.name); + SanckbarWidget( + context, + "Nama, Tanggal Lahir, dan Jenis Kelamin Haruus Diisi !", + snackbarType.warning); + } else { + var temp = ref.watch(EditDataProvider); + PatientData data = PatientData( + noRM: noRmCtr.text, + saluation: selectedSaluation.value.id, + name: nameCtr.text, + prefix: prefixCtr.text, + suffix: suffixCtr.text, + dob: dobState.value.toString(), + nik: nikCtr.text, + nip: nipCtr.text, + withoutNIK: withoutNIK.value ? "Y" : "N", + gender: selectedGender.value.id.toString(), + address: addressCtr.text, + diagnosis: diagnosisCtr.text, + hp: hpCtr.text, + note: noteCtr.text); + ref.read(EditDataProvider.notifier).state = RegistrationModel( + orderID: temp.orderID, + token: auth?.token ?? "", + patientId: temp.patientId ?? "new", + patientData: data, + specimens: temp.specimens, + bahan: temp.bahan, + tests: temp.tests, + total: temp.total); + ref.read(EditStepProvider.state).update((state) => 1); + } + } + + return Padding( + padding: const EdgeInsets.only(top: 20, bottom: 20, left: 24, right: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 120), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + DropdownMenu( + enabled: false, + menuHeight: + Constant.getActualY(context: context, y: 300), + key: ValueKey(sapaanKey.value), + initialSelection: selectedSaluation.value, + trailingIcon: getFilterLoading.value + ? LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20) + : null, + width: Constant.getActualX(context: context, x: 120), + hintText: "Sapaan", + textStyle: Constant.body2_400(context: context), + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + )), + label: const Text( + "Sapaan", + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + onSelected: (value) { + onSelectSaluation(value!); + selectedSaluation.value = value; + }, + dropdownMenuEntries: sapaan.value + .map>( + (e) => DropdownMenuEntry( + value: e, label: e.name ?? "")) + .toList()), + ], + )), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 160), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + disable: false, + hintText: 'Prefix', + labelText: "Prefix", + controller: prefixCtr, + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 520), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + disable: false, + hintText: 'Name', + labelText: "Nama", + controller: nameCtr, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 160), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + disable: false, + hintText: 'Suffix', + labelText: "suffix", + controller: suffixCtr, + ), + ), + ], + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Row( + children: [ + Radio( + value: "date", + groupValue: dobRb.value, + onChanged: (value) { + dobRb.value = value!; + }, + ), + SizedBox(width: Constant.getActualX(context: context, x: 20)), + SizedBox( + width: Constant.getActualX(context: context, x: 252), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + TextField( + enabled: false, + controller: dobCtr, + style: Constant.body2_400(context: context), + decoration: InputDecoration( + suffixIcon: Icon(Icons.calendar_today), + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + ), + labelText: "Tanggal Lahir" //label text of field + ), + readOnly: true, + onTap: () async { + DateTime? pickedDate = await showDatePicker( + // locale: Locale("id"), + context: context, + initialDate: dobState.value, + firstDate: DateTime(1800), + initialEntryMode: DatePickerEntryMode.calendarOnly, + + //DateTime.now() - not to allow to choose before today. + lastDate: DateTime(2100)); + + if (pickedDate != null) { + print( + pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000 + String formattedDate = + DateFormat('dd-MM-yyyy').format(pickedDate); + print( + formattedDate); //formatted date output using intl package => 2021-03-16 + dobCtr.text = + formattedDate; //set output date to TextField value. + dobState.value = pickedDate; + } else {} + }, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + Radio( + value: "age", + groupValue: dobRb.value, + onChanged: (value) { + dobRb.value = value!; + }, + ), + SizedBox(width: Constant.getActualX(context: context, x: 20)), + Stack( + clipBehavior: Clip.none, + // alignment: AlignmentDirectional.bottomCenter, + children: [ + Container( + padding: EdgeInsets.symmetric( + horizontal: + Constant.getActualX(context: context, x: 16), + vertical: Constant.getActualY(context: context, y: 12)), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey)), + child: Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 90), + height: Constant.getActualY(context: context, y: 40), + child: CustomTextField( + onChange: (value) { + if (dobRb.value == "age") { + DateTime coba = Jiffy.now() + .subtract( + months: int.parse(monthCtr.text), + years: int.parse(yearCtr.text), + days: int.parse(dayCtr.text)) + .dateTime; + // print(coba); + dobState.value = coba; + dobCtr.text = DateFormat('dd-MM-yyyy') + .format(dobState.value); + } + }, + disable: dobRb.value != "date", + hintText: 'Tahun', + labelText: "", + isDense: true, + suffixIcon: SizedBox( + child: Container( + padding: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 8), + bottom: Constant.getActualY( + context: context, y: 8), + right: Constant.getActualX( + context: context, x: 5), + ), + child: Text( + 'Tahun', + style: Constant.body3_400(context: context) + .copyWith(color: Colors.grey), + ), + ), + ), + style: Constant.body3_400(context: context), + hintStyle: Constant.body3_400(context: context), + labelStyle: Constant.body3_400(context: context), + controller: yearCtr, + inputType: TextInputType.number, + ), + ), + SizedBox( + width: + Constant.getActualX(context: context, x: 16)), + SizedBox( + width: Constant.getActualX(context: context, x: 90), + height: Constant.getActualY(context: context, y: 40), + child: CustomTextField( + onChange: (value) { + if (dobRb.value == "age") { + DateTime currentDate = DateTime.now(); + DateTime coba = Jiffy.now() + .subtract( + months: int.parse(monthCtr.text), + years: int.parse(yearCtr.text), + days: int.parse(dayCtr.text)) + .dateTime; + // print(coba); + + dobState.value = coba; + dobCtr.text = DateFormat('dd-MM-yyyy') + .format(dobState.value); + } + }, + disable: dobRb.value != "date", + hintText: 'Bulan', + labelText: "", + controller: monthCtr, + inputType: TextInputType.number, + isDense: true, + suffixIcon: Container( + child: Container( + padding: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 8), + bottom: Constant.getActualY( + context: context, y: 8), + right: Constant.getActualX( + context: context, x: 5), + ), + child: Text( + 'Bulan', + style: Constant.body3_400(context: context) + .copyWith(color: Colors.grey), + ), + ), + ), + style: Constant.body3_400(context: context), + hintStyle: Constant.body3_400(context: context), + labelStyle: Constant.body3_400(context: context), + ), + ), + SizedBox( + width: + Constant.getActualX(context: context, x: 16)), + SizedBox( + width: Constant.getActualX(context: context, x: 90), + height: Constant.getActualY(context: context, y: 40), + child: CustomTextField( + disable: dobRb.value != "date", + onChange: (value) { + if (dobRb.value == "age") { + if (int.parse(dayCtr.text) <= 31) { + DateTime currentDate = DateTime.now(); + + DateTime coba = Jiffy.now() + .subtract( + months: int.parse(monthCtr.text), + years: int.parse(yearCtr.text), + days: int.parse(dayCtr.text)) + .dateTime; + // print(coba); + + dobState.value = coba; + dobCtr.text = DateFormat('dd-MM-yyyy') + .format(dobState.value); + } else { + dayCtr.text = "31"; + SanckbarWidget( + context, + "hari tidak lebih dari 31", + snackbarType.warning); + } + } + }, + hintText: 'Hari', + labelText: "", + controller: dayCtr, + inputType: TextInputType.number, + isDense: true, + suffixIcon: Container( + child: Container( + padding: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 8), + bottom: Constant.getActualY( + context: context, y: 8), + right: Constant.getActualX( + context: context, x: 5), + ), + child: Text( + 'Hari', + style: Constant.body3_400(context: context) + .copyWith(color: Colors.grey), + ), + ), + ), + style: Constant.body3_400(context: context), + hintStyle: Constant.body3_400(context: context), + labelStyle: Constant.body3_400(context: context), + ), + ), + ], + ), + ), + Positioned( + top: -8, + left: 10, + child: Container( + color: Colors.white, + child: const Text( + "Umur", + style: TextStyle(color: Colors.grey), + ))) + ], + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 300), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + disable: false, + hintText: 'No RM', + labelText: "No RM", + controller: noRmCtr, + ), + ) + ], + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 240), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + disable: false, + inputType: TextInputType.number, + controller: nikCtr, + hintText: withoutNIK.value + ? "Pasien Tanpa NIK Data Historis Tidak Terkonsolidasi" + : 'NIK', + labelText: withoutNIK.value + ? "Pasien Tanpa NIK Data Historis Tidak Terkonsolidasi" + : "NIK"), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 20)), + Center( + child: Transform.scale( + scale: 0.8, + child: Checkbox( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4)), + value: withoutNIK.value, + onChanged: (value) { + withoutNIK.value = value!; + if (value == true) {} + }), + ), + ), + Text('Tanpa NIK', + style: Constant.body2_400(context: context) + .copyWith(color: Constant.textBlack)), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 24), + ), + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 304), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + DropdownMenu( + enabled: false, + key: ValueKey(genderKey.value), + initialSelection: selectedGender.value, + trailingIcon: getFilterLoading.value + ? LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20) + : null, + width: Constant.getActualX(context: context, x: 304), + hintText: "Jenis Kelamin", + textStyle: Constant.body2_400(context: context), + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + )), + label: const Text("jenis Kelamin"), + onSelected: (value) { + onSelectGender(value!); + selectedGender.value = value; + }, + dropdownMenuEntries: gender.value + .map>( + (e) => DropdownMenuEntry( + value: e, label: e.name ?? "")) + .toList()), + ], + )), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + disable: false, + hintText: 'No Hp', + labelText: "No Hp", + controller: hpCtr, + ), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + child: CustomTextField( + disable: false, + hintText: "NIP", + labelText: "NIP", + controller: nipCtr, + ), + ), + ], + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 304), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + disable: false, + hintText: 'Jabatan', + labelText: "Jabatan", + controller: jabatanCtr, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + disable: false, + hintText: 'Kedudukan', + labelText: "Kedudukan", + controller: kedudukanCtr, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + disable: false, + hintText: 'Lokasi', + labelText: "Lokasi", + controller: lokasiCtr, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + disable: false, + hintText: 'Pekerjaan', + labelText: "Pekerjaan", + controller: pekerjaanCtr, + ), + ], + ), + ) + ], + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 1032), + // height: Constant.getActualY(context: context, y: 120), + child: CustomTextField( + disable: false, + controller: addressCtr, + hintText: 'Alamat', + labelText: "Alamat", + maxLines: 4, + minLines: 1), + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 1032), + // height: Constant.getActualY(context: context, y: 120), + child: CustomTextField( + controller: diagnosisCtr, + hintText: 'Diagnosis', + labelText: "Diagnosis", + maxLines: 4, + minLines: 1), + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 1032), + // height: Constant.getActualY(context: context, y: 120), + child: CustomTextField( + controller: noteCtr, + hintText: 'Catatan FO', + labelText: "Catatan FO", + maxLines: 4, + minLines: 1), + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + SizedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + OutlinedButton( + style: OutlinedButton.styleFrom( + backgroundColor: Colors.white, + side: BorderSide(color: Colors.grey.shade400, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: () { + ref.read(EditDataProvider.notifier).state = + RegistrationModel(); + ref.read(selectedTestEditProvider.notifier).state = + List.empty(growable: true); + ref.read(currentPacketProvider.notifier).state = + List.empty(growable: true); + ref.read(selectedPacketEditProvider.notifier).state = + List.empty(); + Navigator.pop(context); + }, + child: SizedBox( + height: Constant.getActualY(context: context, y: 56), + child: Center( + child: Text('Batal', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack)), + ), + )), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Constant.green, + side: BorderSide(color: Constant.green, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + onPressed: () { + nextStep(); + }, + child: SizedBox( + height: Constant.getActualY(context: context, y: 56), + child: Center( + child: Text('Berikutnya', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textWhite)), + ), + )), + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/screen/order/order_filter.dart b/lib/screen/order/order_filter.dart new file mode 100644 index 0000000..656d890 --- /dev/null +++ b/lib/screen/order/order_filter.dart @@ -0,0 +1,214 @@ +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/intl.dart'; + +import '../../app/constant.dart'; +import '../../provider/auth_provider.dart'; +import '../../widgets/custom_dialog_builder.dart'; + +class OrderFilter extends HookConsumerWidget { + const OrderFilter({ + super.key, + required this.startDateCtr, + required this.startDateState, + required this.endDateCtr, + required this.endDateState, + required this.keywordCtr, + }); + + final TextEditingController startDateCtr; + final ValueNotifier startDateState; + final TextEditingController endDateCtr; + final ValueNotifier endDateState; + final TextEditingController keywordCtr; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final auth = ref.watch(authProvider); + final username = auth?.mUserUsername ?? "-"; + + showDialogBuilder() { + var endDateParam = + DateFormat('yyyy-MM-dd').format(endDateState.value).toString(); + var startDateParam = + DateFormat('yyyy-MM-dd').format(startDateState.value).toString(); + var companyID = auth?.mUserMCompanyID ?? ""; + print("endDateParam : $endDateParam"); + print("startDateParam : $startDateParam"); + + CustomDialogBuilder.dialogBuilderWebPDF( + context, + // startDateCtr.text.toString(), + // endDateCtr.text.toString(), + startDateParam, + endDateParam, + keywordCtr.text.toString(), + username, + companyID); + // launchUrlString("http://10.9.8.249/birt/run?__report=report/one/rekap/rpt_sales_001.rptdesign&__format=xls&PStartDate=2023-01-01&PEndDate=2023-01-31&username=adminsas&tm=1681262919125"); + } + + return Padding( + padding: const EdgeInsets.all(0), + child: Row( + children: [ + Expanded( + flex: 3, + child: TextField( + style: Constant.body1_600(context: context), + controller: startDateCtr, + decoration: InputDecoration( + suffixIcon: Icon(EvaIcons.calendar), + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + ), + labelStyle: Constant.body1_600(context: context), + labelText: "Tanggal Awal" //label text of field + ), + readOnly: true, + onTap: () async { + DateTime? pickedDate = await showDatePicker( + // locale: Locale("id"), + confirmText: "OK", + cancelText: "Batal", + context: context, + initialDate: startDateState.value, + firstDate: DateTime(1800), + initialEntryMode: DatePickerEntryMode.calendarOnly, + + //DateTime.now() - not to allow to choose before today. + lastDate: DateTime(2100)); + + if (pickedDate != null) { + print( + pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000 + String formattedDate = + DateFormat('dd-MM-yyyy').format(pickedDate); + print( + formattedDate); //formatted date output using intl package => 2021-03-16 + startDateCtr.text = + formattedDate; //set output date to TextField value. + startDateState.value = pickedDate; + } else {} + }, + ), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + Expanded( + flex: 3, + child: TextField( + style: Constant.body1_600(context: context), + controller: endDateCtr, + decoration: InputDecoration( + suffixIcon: Icon(EvaIcons.calendar), + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + ), + labelStyle: Constant.body1_600(context: context), + labelText: "Tanggal Akhir" //label text of field + ), + readOnly: true, + onTap: () async { + DateTime? pickedDate = await showDatePicker( + // locale: Locale("id"), + confirmText: "OK", + cancelText: "Batal", + context: context, + initialDate: endDateState.value, + firstDate: DateTime(1800), + initialEntryMode: DatePickerEntryMode.calendarOnly, + + //DateTime.now() - not to allow to choose before today. + lastDate: DateTime(2100)); + + if (pickedDate != null) { + print( + pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000 + String formattedDate = + DateFormat('dd-MM-yyyy').format(pickedDate); + print( + formattedDate); //formatted date output using intl package => 2021-03-16 + endDateCtr.text = + formattedDate; //set output date to TextField value. + endDateState.value = pickedDate; + } else {} + }, + ), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + Expanded( + flex: 6, + child: TextField( + style: Constant.body1_600(context: context), + controller: keywordCtr, + decoration: InputDecoration( + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + ), + hintText: "No Reg / Nama", + hintStyle: Constant.body1_600(context: context), + labelStyle: Constant.body1_600(context: context), + labelText: "No Reg / Nama" //label text of field + )), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + // button report pdf + Expanded( + flex: 1, + child: SizedBox( + width: Constant.getActualX( + context: context, + x: 171, + ), + height: Constant.getActualY(context: context, y: 56), + child: ElevatedButton( + onPressed: () { + showDialogBuilder(); + }, + style: ButtonStyle( + backgroundColor: WidgetStateColor.resolveWith( + (st) => Constant.primaryRed, + ), + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide( + color: Colors.transparent, + ), + ), + ), + // shadowColor: MaterialStateProperty.all( + // Color(0xffff48423d)), + elevation: WidgetStateProperty.all(1.0), + ), + // child: Align( + // alignment: Alignment.center, + // child: Text( + // 'Cetak', + // style: Constant.body3_600(context: context).copyWith( + // color: Constant.textWhite, + // ), + // ), + // ), + child: Icon( + color: Colors.white, + EvaIcons.printerOutline, + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/screen/order/order_screen.dart b/lib/screen/order/order_screen.dart new file mode 100644 index 0000000..9f39566 --- /dev/null +++ b/lib/screen/order/order_screen.dart @@ -0,0 +1,1022 @@ +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/intl.dart'; +import 'package:mitra_corporate/model/registration_model.dart'; +import 'package:mitra_corporate/model/search_order_model.dart'; +import 'package:mitra_corporate/model/test_model.dart'; +import 'package:mitra_corporate/provider/order_provider.dart'; +import 'package:mitra_corporate/screen/order/cancel_order_provider.dart'; +import 'package:mitra_corporate/screen/order/dialog_cancel_order.dart'; +import 'package:mitra_corporate/screen/order/dialog_edit.dart'; +import 'package:mitra_corporate/screen/order/search_order_provider.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../provider/auth_provider.dart'; +import '../../provider/registrasi_provider.dart'; +import '../../widgets/custom_snackbar_widget.dart'; +import '../../widgets/header.dart'; +import '../../widgets/qr_status_chip.dart'; +import '../registrasi/registrasi_prvider.dart'; +import '../registrasi/table_pasien.dart'; +import 'order_filter.dart'; + +class OrderScreen extends HookConsumerWidget { + const OrderScreen({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final startDateCtr = useTextEditingController( + text: DateFormat('dd-MM-yyyy').format(DateTime.now())); + final startDateState = useState(DateTime.now()); + final endDateCtr = useTextEditingController( + text: DateFormat('dd-MM-yyyy').format(DateTime.now())); + final endDateState = useState(DateTime.now()); + final keywordCtr = useTextEditingController(text: ""); + final auth = ref.watch(authProvider); + final keyword = useState(""); + final rowsPerPage = useState(10); + final searchLoading = useState(false); + final cancelLoading = useState(false); + final dataStart = useState(1); + final dataEnd = useState(1); + final currPage = useState(1); + final listTest = useState>(List.empty()); + final orderList = useState( + SearchOrderModel(orders: [], total: "0", totalPage: 0)); + keywordCtr.addListener( + () { + keyword.value = keywordCtr.text; + }, + ); + refresh() { + var ed = + "${DateFormat('yyyy-MM-dd').format(endDateState.value)} 23:59:59"; + var sd = + "${DateFormat('yyyy-MM-dd').format(startDateState.value)} 00:00:00"; + print("ed : $ed"); + print("sd : $sd"); + + ref.read(SearchOrderProvider.notifier).getData( + token: auth?.token ?? "", + companyID: auth?.mUserMCompanyID ?? "", + rpp: rowsPerPage.value.toString(), + keyword: keyword.value, + page: currPage.value.toString(), + startDate: sd, + endDate: ed); + } + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final debouncer = Debouncer(milliseconds: 100); + debouncer.run(() { + // ref.read(SearchPatientProvider.notifier).getData( + // token: auth?.token ?? "", + // page: currPage.value.toString(), + // keyword: keyword, + // rpp: rowsPerPage.value.toString(), + // companyID: auth?.mUserMCompanyID ?? ""); + + final debouncer = Debouncer(milliseconds: 100); + var ed = + "${DateFormat('yyyy-MM-dd').format(endDateState.value)} 23:59:59"; + var sd = + "${DateFormat('yyyy-MM-dd').format(startDateState.value)} 00:00:00"; + print("ed : $ed"); + print("sd : $sd"); + // debouncer.run(() { + ref.read(SearchOrderProvider.notifier).getData( + token: auth?.token ?? "", + companyID: auth?.mUserMCompanyID ?? "", + rpp: rowsPerPage.value.toString(), + keyword: keyword.value, + page: currPage.value.toString(), + startDate: sd, + endDate: ed); + // }); + }); + }); + return () {}; + }, [currPage.value]); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final debouncer = Debouncer(milliseconds: 100); + currPage.value = 1; + var ed = + "${DateFormat('yyyy-MM-dd').format(endDateState.value)} 23:59:59"; + var sd = + "${DateFormat('yyyy-MM-dd').format(startDateState.value)} 00:00:00"; + print("ed : $ed"); + print("sd : $sd"); + debouncer.run(() { + ref.read(SearchOrderProvider.notifier).getData( + token: auth?.token ?? "", + companyID: auth?.mUserMCompanyID ?? "", + rpp: rowsPerPage.value.toString(), + keyword: keyword.value, + page: "1", + startDate: sd, + endDate: ed); + }); + }); + return () {}; + }, [ + keyword.value, + rowsPerPage.value, + startDateState.value, + endDateState.value + ]); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + ref.read(GetFPPProvider.notifier).getData( + mouID: auth?.mUserMMouID ?? "0", token: auth?.token ?? "0"); + }); + return () {}; + }, []); + + ref.listen( + SearchOrderProvider, + (pref, next) { + if (next is SearchOrderStateInit) { + searchLoading.value = true; + } else if (next is SearchOrderStateLoading) { + searchLoading.value = true; + } else if (next is SearchOrderStateError) { + searchLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is SearchOrderStateDone) { + orderList.value = next.model; + // print(jsonEncode(next.model)); + + dataStart.value = + (currPage.value * rowsPerPage.value) - rowsPerPage.value + 1; + dataEnd.value = currPage.value == next.model.totalPage + ? int.parse(next.model.total ?? "0") + : dataStart.value + rowsPerPage.value - 1; + + searchLoading.value = false; + } + }, + ); + ref.listen( + GetFPPProvider, + (pref, next) { + if (next is GetFPPStateInit) { + searchLoading.value = true; + } else if (next is GetFPPStateLoading) { + searchLoading.value = true; + } else if (next is GetFPPStateError) { + searchLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is GetFPPStateDone) { + listTest.value = next.model; + // print(jsonEncode(next.model)); + + // tabController.length + + searchLoading.value = false; + } + }, + ); + + cancelOrder(orderID) { + ref + .read(CancelOrderProvider.notifier) + .cancel(token: auth?.token ?? "0", orderID: orderID); + } + + ref.listen( + CancelOrderProvider, + (pref, next) { + if (next is CancelOrderStateInit) { + cancelLoading.value = true; + } else if (next is CancelOrderStateLoading) { + cancelLoading.value = true; + } else if (next is CancelOrderStateError) { + cancelLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is CancelOrderStateDone) { + refresh(); + Navigator.pop(context); + // print(jsonEncode(next.model)); + SanckbarWidget( + context, "Berhasil Membatalkan order", snackbarType.success); + cancelLoading.value = false; + } + }, + ); + + return Material( + child: Column(children: [ + Header(), + Padding( + padding: const EdgeInsets.all(32.0), + child: Column( + children: [ + Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 1079), + height: Constant.getActualY(context: context, y: 40), + child: Text('Order', + style: Constant.h3_400(context: context) + .copyWith(color: Constant.textBlack)), + ), + SizedBox(height: Constant.getActualY(context: context, y: 42)), + Container( + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(12)), + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 32), + ), + child: Column( + children: [ + OrderFilter( + startDateCtr: startDateCtr, + startDateState: startDateState, + endDateCtr: endDateCtr, + endDateState: endDateState, + keywordCtr: keywordCtr, + ), + SizedBox( + height: Constant.getActualY(context: context, y: 24)), + Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8)), + child: Column( + children: [ + Row( + children: [ + Expanded( + flex: 1, + child: Padding( + padding: const EdgeInsets.all(0), + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 24, + right: 6), + child: Text('No', + overflow: TextOverflow.ellipsis, + style: Constant.body3_600( + context: context) + .copyWith( + color: Constant.textGrey)), + ), + ), + )), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 10, right: 6), + child: Text('Nomor Reg', + overflow: TextOverflow.ellipsis, + style: Constant.body3_600( + context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 6, right: 6), + child: Text('Nama', + overflow: TextOverflow.ellipsis, + style: Constant.body3_600( + context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 6, right: 24), + child: Text('No. Hp', + overflow: TextOverflow.ellipsis, + style: Constant.body3_600( + context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 3, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 6, right: 6), + child: Text('Pemeriksaan', + overflow: TextOverflow.ellipsis, + style: Constant.body3_600( + context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 6, right: 6), + child: Text('QR Code', + overflow: TextOverflow.ellipsis, + style: Constant.body3_600( + context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 6, right: 6), + child: Text('Status Registrasi', + overflow: TextOverflow.ellipsis, + maxLines: 2, + style: Constant.body3_600( + context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 6, right: 6), + child: Text('Status Proses', + overflow: TextOverflow.ellipsis, + maxLines: 2, + style: Constant.body3_600( + context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 6, right: 6), + child: Text('Status Hasil', + overflow: TextOverflow.ellipsis, + maxLines: 2, + style: Constant.body3_600( + context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 12, right: 12), + child: Text('Aksi', + overflow: TextOverflow.ellipsis, + style: Constant.body3_600( + context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ) + ], + ), + Divider(), + Container( + color: Colors.white, + height: Constant.getActualY(context: context, y: 400), + child: searchLoading.value + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 40)) + : ListView( + children: orderList.value.orders! + .asMap() + .entries + .map((e) => Column( + children: [ + TextButton( + style: TextButton.styleFrom( + shape: + RoundedRectangleBorder( + borderRadius: + BorderRadius + .circular( + 8)), + foregroundColor: + Constant.primaryBlue), + onPressed: e.value.status == 'N' + ? () {} + : null, + child: Row( + children: [ + Expanded( + flex: 1, + child: Padding( + padding: + const EdgeInsets + .all(8.0), + child: Container( + alignment: Alignment + .centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Colors.white, + child: Padding( + padding: + const EdgeInsets + .only( + top: 12, + bottom: + 12, + left: 6, + right: + 6), + child: Text( + (e.key + + dataStart + .value) + .toString(), + // overflow: + // TextOverflow + // .ellipsis, + style: Constant.body3_600( + context: + context) + .copyWith( + color: + Colors.black), + )), + ), + )), + Expanded( + flex: 2, + child: Container( + alignment: Alignment + .centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Colors.white, + child: Padding( + padding: + const EdgeInsets + .only( + top: 12, + bottom: 12, + left: 6, + right: 6), + child: Row( + children: [ + Text( + e.value.orderNumber ?? + "", + style: Constant.body3_600( + context: + context) + .copyWith( + color: + Colors.black)), + ], + ), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment + .centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Colors.white, + child: Padding( + padding: + const EdgeInsets + .only( + top: 12, + bottom: 12, + left: 6, + right: 6), + child: Text( + e.value.patientName ?? + "", + style: Constant + .body3_600( + context: + context) + .copyWith( + color: Colors + .black)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment + .centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Colors.white, + child: Padding( + padding: + const EdgeInsets + .only( + top: 12, + bottom: 12, + left: 6, + right: 6), + child: Text( + e.value.hp ?? "", + style: Constant + .body3_600( + context: + context) + .copyWith( + color: Colors + .black)), + ), + ), + ), + Expanded( + flex: 3, + child: Container( + alignment: Alignment + .centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: + const EdgeInsets + .only( + top: 12, + bottom: 12, + left: 6, + right: 6), + child: Wrap( + runSpacing: 10, + spacing: 10, + children: + e.value.tests! + .map( + (j) => Chip( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + label: Text( + j, + style: Constant.caption1_600(context: context), + )), + ) + .toList(), + )), + ), + ), + //Status Qr + Expanded( + flex: 2, + child: Container( + alignment: Alignment + .centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: + const EdgeInsets + .only( + top: 12, + bottom: 12, + left: 6, + right: 6), + child: e.value + .isQr == + "Y" + ? QRStatusChip( + status: e + .value + .statusQr!, + ) + : null), + ), + ), + // Status registrasi + Expanded( + flex: 2, + child: Container( + alignment: Alignment + .centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: + const EdgeInsets + .only( + top: 12, + bottom: 12, + left: 6, + right: 6), + child: e.value + .statusPemeriksaan == + 'Y' || + e.value.statusPemeriksaan == + 'P' || + e.value.statusPemeriksaan == + 'R' + ? Tooltip( + message: + "Sudah Registrasi", + child: Icon( + EvaIcons + .checkmarkCircle2, + color: Colors + .green, + ), + ) + : null, + ), + ), + ), + //Status proses + Expanded( + flex: 2, + child: Container( + alignment: Alignment + .centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: + const EdgeInsets + .only( + top: 12, + bottom: 12, + left: 6, + right: 6), + child: e.value.statusPemeriksaan == + 'P' || + e.value.statusPemeriksaan == + 'R' + ? Tooltip( + message: + "Sudah Di proses", + child: Icon( + EvaIcons + .checkmarkCircle2, + color: Colors + .green, + ), + ) + : null, + ), + ), + ), + //status hasil + Expanded( + flex: 2, + child: Container( + alignment: Alignment + .centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: + const EdgeInsets + .only( + top: 12, + bottom: 12, + left: 6, + right: 6), + child: + e.value.statusPemeriksaan == + 'R' + ? Tooltip( + message: + "Sudah Ada Hasil", + child: + Icon( + EvaIcons + .checkmarkCircle2, + color: Colors + .green, + ), + ) + : null, + ), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: + Alignment.center, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: + (e.value.status != + 'N') + ? Tooltip( + message: + "${e.value.orderNumber} sudah dibuat surat jalan", + child: Icon( + EvaIcons + .lockOutline, + color: Colors + .red, + ), + ) + : Wrap( + children: [ + Tooltip( + message: + "Edit order ${e.value.orderNumber}", + child: IconButton( + onPressed: () { + ref.read(dialogOrderActionProvider.notifier).state = 'edit'; + ref.read(selectedPacketEditProvider.notifier).state = e.value.paket ?? []; + ref.read(currentPacketProvider.notifier).state = e.value.paketDetail ?? []; + List tests = List.empty(growable: true); + for (var element in listTest.value) { + element.items?.forEach((i) { + bool cek = e.value.testsID!.contains(i.testID); + if (cek) { + var ts = "${element.tabId}|${i.testID}"; + tests.add(ts); + } + }); + } + List sp = List.empty(growable: true); + e.value.sample?.forEach((element) { + List splitted = element.split('|'); + if (splitted.length > 3) { + sp.add(Specimens(detailID: splitted[0], id: splitted[1], name: splitted[2], amount: splitted[3], ctr: TextEditingController(text: splitted[3]))); + } + }); + List bhn = List.empty(growable: true); + e.value.bahan?.forEach((element) { + List splitted = element.split('|'); + if (splitted.length > 3) { + bhn.add(Bahan(detailID: splitted[0], id: splitted[1], name: splitted[2], amount: splitted[3], ctr: TextEditingController(text: splitted[3]))); + } + }); + ref.read(selectedTestEditProvider.notifier).state = tests; + ref.read(selectedtestData.notifier).state = e.value.testDetail ?? []; + RegistrationModel a = RegistrationModel( + orderID: e.value.orderId, + specimens: sp, + bahan: bhn, + patientId: e.value.patientId, + patientData: PatientData( + noRM: e.value.noRM, + address: e.value.address, + diagnosis: e.value.diagnosis, + dob: e.value.dob, + gender: e.value.sexID, + hp: e.value.hp, + name: e.value.patientName, + nik: e.value.NIK, + nip: e.value.NIP, + note: e.value.note, + prefix: e.value.prefix, + saluation: e.value.title, + suffix: e.value.suffix, + withoutNIK: 'N', + jabatan: e.value.jabatan, + kedudukan: e.value.kedudukan, + lokasi: e.value.lokasi, + pekerjaan: e.value.pekerjaan, + )); + ref.read(EditDataProvider.notifier).state = a; + showDialog(context: context, barrierDismissible: false, builder: ((context) => DialogEdit())).then((value) => refresh()); + }, + icon: Icon( + EvaIcons.edit2, + color: Constant.textGrey, + )), + ), + Tooltip( + message: + "Batalkan order", + child: IconButton( + onPressed: () { + showDialog( + context: context, + barrierDismissible: !cancelLoading.value, + builder: (BuildContext context) => DialogCancelOrder(cancel: cancelOrder, orderID: e.value.orderId ?? "0", loading: cancelLoading.value, orderNumber: e.value.orderNumber ?? "", name: e.value.patientName ?? ""), + ).whenComplete(() { + // onRefresh(); + }); + }, + icon: Icon( + EvaIcons.closeCircle, + color: Constant.textGrey, + )), + ) + ], + ), + ), + ), + ], + ), + ), + Divider() + ], + )) + .toList(), + ), + ), + Divider( + indent: 0, + endIndent: 0, + height: 0, + thickness: 1, + ), + Container( + color: Colors.white, + height: Constant.getActualY(context: context, y: 60), + child: Row( + children: [ + Spacer(), + Text( + "Rows per page :", + style: Constant.body3_400(context: context), + ), + SizedBox( + width: Constant.getActualX( + context: context, x: 24), + ), + DropdownButton( + value: rowsPerPage.value, + dropdownColor: Colors.white, + elevation: 0, + style: Constant.body3_400(context: context) + .copyWith(color: Colors.black), + underline: Container(), + icon: Icon(Icons.keyboard_arrow_down_rounded), + focusColor: Colors.white, + items: [ + DropdownMenuItem( + value: 10, + child: Text("10"), + ), + DropdownMenuItem( + value: 15, + child: Text("15"), + ), + DropdownMenuItem( + value: 20, + child: Text("20"), + ), + DropdownMenuItem( + value: 30, + child: Text("30"), + ), + DropdownMenuItem( + value: 40, + child: Text("40"), + ), + DropdownMenuItem( + value: 50, + child: Text("50"), + ), + ], + onChanged: (value) { + rowsPerPage.value = value!; + }), + SizedBox( + width: Constant.getActualX( + context: context, x: 24), + ), + Text( + "${dataStart.value} - ${dataEnd.value} of ${orderList.value.total}", + style: Constant.body3_400(context: context), + ), + SizedBox( + width: Constant.getActualX( + context: context, x: 24), + ), + IconButton( + iconSize: 20, + onPressed: currPage.value > 1 + ? () { + currPage.value = currPage.value - 1; + } + : null, + icon: Icon(Icons.arrow_back_ios_new_rounded)), + SizedBox( + width: Constant.getActualX( + context: context, x: 10), + ), + IconButton( + iconSize: 20, + onPressed: currPage.value < + orderList.value.totalPage!.toInt() + ? () { + currPage.value = currPage.value + 1; + } + : null, + icon: Icon(Icons.arrow_forward_ios_outlined)), + SizedBox( + width: Constant.getActualX( + context: context, x: 24), + ) + ], + ), + ) + ], + ), + ) + ], + ), + ), + ], + ), + ), + ])); + } +} diff --git a/lib/screen/order/search_order_provider.dart b/lib/screen/order/search_order_provider.dart new file mode 100644 index 0000000..98c67eb --- /dev/null +++ b/lib/screen/order/search_order_provider.dart @@ -0,0 +1,79 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/search_order_model.dart'; +import 'package:mitra_corporate/repository/order_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class SearchOrderState extends Equatable { + final DateTime date; + const SearchOrderState(this.date); + @override + List get props => [date]; +} + +class SearchOrderStateInit extends SearchOrderState { + SearchOrderStateInit() : super(DateTime.now()); +} + +class SearchOrderStateLoading extends SearchOrderState { + SearchOrderStateLoading() : super(DateTime.now()); +} + +class SearchOrderStateError extends SearchOrderState { + final String message; + SearchOrderStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class SearchOrderStateDone extends SearchOrderState { + final SearchOrderModel model; + SearchOrderStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class SearchOrderNotifier extends StateNotifier { + final Ref ref; + SearchOrderNotifier({ + required this.ref, + }) : super(SearchOrderStateInit()); + + void getData({ + required String token, + required String companyID, + required String rpp, + required String keyword, + required String page, + required String startDate, + required String endDate, + }) async { + try { + state = SearchOrderStateLoading(); + final dio = ref.read(dioProvider); + final resp = await OrderRepository(dio: dio).searchOrder( + endDate: endDate, + startDate: startDate, + token: token, + companyID: companyID, + rpp: rpp, + keyword: keyword, + page: page); + state = SearchOrderStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = SearchOrderStateError(message: e.message.toString()); + } else { + state = SearchOrderStateError(message: e.toString()); + } + } + } +} + +//provider +final SearchOrderProvider = + StateNotifierProvider( + (ref) => SearchOrderNotifier(ref: ref)); diff --git a/lib/screen/registrasi/delete_patient_provider copy.dart b/lib/screen/registrasi/delete_patient_provider copy.dart new file mode 100644 index 0000000..14578e6 --- /dev/null +++ b/lib/screen/registrasi/delete_patient_provider copy.dart @@ -0,0 +1,65 @@ +import 'package:mitra_corporate/repository/patient_repository.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class DeletePatientProviderState extends Equatable { + final DateTime date; + const DeletePatientProviderState(this.date); + @override + List get props => [date]; +} + +class DeletePatientProviderStateInit extends DeletePatientProviderState { + DeletePatientProviderStateInit() : super(DateTime.now()); +} + +class DeletePatientProviderStateLoading extends DeletePatientProviderState { + DeletePatientProviderStateLoading() : super(DateTime.now()); +} + +class DeletePatientProviderStateError extends DeletePatientProviderState { + final String message; + DeletePatientProviderStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class DeletePatientProviderStateDone extends DeletePatientProviderState { + final String msg; + DeletePatientProviderStateDone({ + required this.msg, + }) : super(DateTime.now()); +} + +//notifier +class DeletePatientProviderNotifier + extends StateNotifier { + final Ref ref; + DeletePatientProviderNotifier({ + required this.ref, + }) : super(DeletePatientProviderStateInit()); + + void DeletePatent({required String patient_id, required String token}) async { + try { + state = DeletePatientProviderStateLoading(); + final dio = ref.read(dioProvider); + final resp = await PatientRepository(dio: dio) + .deletePatient(patient_id: patient_id, token: token); + state = DeletePatientProviderStateDone(msg: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = DeletePatientProviderStateError(message: e.message.toString()); + } else { + state = DeletePatientProviderStateError(message: e.toString()); + } + } + } +} + +//provider +final DeletePatientProvider = StateNotifierProvider< + DeletePatientProviderNotifier, DeletePatientProviderState>( + (ref) => DeletePatientProviderNotifier(ref: ref)); diff --git a/lib/screen/registrasi/dialog_delete_patient.dart b/lib/screen/registrasi/dialog_delete_patient.dart new file mode 100644 index 0000000..baae00b --- /dev/null +++ b/lib/screen/registrasi/dialog_delete_patient.dart @@ -0,0 +1,156 @@ +import 'package:flutter/material.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; + +class DialogDeletePatient extends StatelessWidget { + const DialogDeletePatient( + {super.key, + required this.NIK, + required this.delete, + required this.patientID, + required this.loading, + required this.name}); + + final String name; + final String NIK; + final Function delete; + final String patientID; + final bool loading; + + @override + Widget build(BuildContext context) { + return AlertDialog( + titlePadding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 24)), + actionsPadding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 24)), + contentPadding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 24)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8))), + title: Text( + 'Konfirmasi hapus', + style: Constant.h4_600(context: context), + ), + content: Container( + constraints: BoxConstraints( + minHeight: Constant.getActualY(context: context, y: 100), + maxHeight: Constant.getActualY(context: context, y: 362), + ), + height: Constant.getActualY(context: context, y: 240), + child: Column( + children: [ + Text( + 'Anda yakin menghapus pasien berikut ?', + style: Constant.body1_600(context: context), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 24), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 416), + child: Card( + elevation: 1, + shape: RoundedRectangleBorder( + side: BorderSide(color: Constant.grey_200), + borderRadius: BorderRadius.circular(12)), + child: Container( + padding: EdgeInsets.symmetric( + horizontal: + Constant.getActualX(context: context, x: 24), + vertical: + Constant.getActualY(context: context, y: 24)), + child: Column( + children: [ + Row( + children: [ + Expanded( + flex: 5, + child: Text( + "NIK", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + ), + Expanded( + flex: 7, + child: Text( + NIK, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack), + ), + ) + ], + ), + SizedBox( + height: + Constant.getActualY(context: context, y: 12), + ), + Row( + children: [ + Expanded( + flex: 5, + child: Text( + "Nama", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + ), + Expanded( + flex: 7, + child: Text( + name, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack), + ), + ) + ], + ), + ], + )), + ), + ) + ], + )), + actions: [ + OutlinedButton( + style: OutlinedButton.styleFrom( + surfaceTintColor: Constant.primaryBlue, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: !loading ? () => Navigator.pop(context, 'Batal') : null, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: loading + ? LoadingAnimationWidget.staggeredDotsWave( + color: Constant.primaryBlue, size: 20) + : Text('Batal', + style: Constant.button_medium(context: context) + .copyWith(color: Constant.textBlack)), + ), + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: !loading ? () => delete(patientID) : null, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: loading + ? LoadingAnimationWidget.staggeredDotsWave( + color: Colors.white, size: 20) + : Text( + 'Yakin', + style: Constant.button_medium(context: context), + ), + ), + ), + ], + ); + } +} diff --git a/lib/screen/registrasi/dialog_edit_patient.dart b/lib/screen/registrasi/dialog_edit_patient.dart new file mode 100644 index 0000000..ef23583 --- /dev/null +++ b/lib/screen/registrasi/dialog_edit_patient.dart @@ -0,0 +1,951 @@ +import 'dart:convert'; + +import 'package:mitra_corporate/provider/registrasi_provider.dart'; +import 'package:mitra_corporate/screen/registrasi/edit_patient_provider.dart'; +import 'package:age_calculator/age_calculator.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/intl.dart'; +import 'package:jiffy/jiffy.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../model/registration_model.dart'; +import '../../provider/auth_provider.dart'; +import '../../widgets/custom_snackbar_widget.dart'; +import '../../widgets/custom_text_field.dart'; +import 'get_filter_provider.dart'; + +class DialogEditPatient extends HookConsumerWidget { + const DialogEditPatient({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final tempPatient = ref.watch(registrationEditPatientProvider); + final auth = ref.watch(authProvider); + //withoutnik apabila tanpa nik maka true + final withoutNIK = useState(false); + final getFilterLoading = useState(false); + final sapaanKey = useState(1); + final genderKey = useState(1000); + final prefixCtr = useTextEditingController(text: ""); + final nameCtr = useTextEditingController(text: ""); + final suffixCtr = useTextEditingController(text: ""); + final nikCtr = useTextEditingController(text: ""); + final nipCtr = useTextEditingController(text: ""); + final hpCtr = useTextEditingController(text: ""); + final noRmCtr = useTextEditingController(text: ""); + final addressCtr = useTextEditingController(text: ""); + final diagnosisCtr = useTextEditingController(text: ""); + final noteCtr = useTextEditingController(text: ""); + final jabatanCtr = useTextEditingController(text: ""); + final kedudukanCtr = useTextEditingController(text: ""); + final lokasiCtr = useTextEditingController(text: ""); + final pekerjaanCtr = useTextEditingController(text: ""); + final sapaan = useState>(List.empty()); + final selectedSaluation = useState(CustomDropDownModel()); + final gender = useState>(List.empty()); + final selectedGender = useState(CustomDropDownModel()); + final editLoading = useState(false); + //select salah satu sapaan + onSelectSaluation(CustomDropDownModel value) { + selectedSaluation.value = value; + selectedGender.value = + gender.value.firstWhere((element) => element.id == value.type); + + genderKey.value = genderKey.value + 1; + // sapaanKey.value = sapaanKey.value + 1; + } + + //update gender saat sapaan dipilih + selectedSaluation.addListener(() { + selectedGender.value = gender.value + .firstWhere((element) => element.id == selectedSaluation.value.type); + genderKey.value = genderKey.value + 1; + }); + + //select gender + onSelectGender(CustomDropDownModel value) { + selectedGender.value = value; + } + + final dobRb = useState('date'); + final dobCtr = useTextEditingController( + text: DateFormat('dd-MM-yyyy').format(DateTime.now())); + final dobState = useState(DateTime.now()); + + final yearCtr = useTextEditingController(text: "0"); + final monthCtr = useTextEditingController(text: "0"); + final dayCtr = useTextEditingController(text: "0"); + + //perhitungan umur dari tanggal ke angka + DateDuration calculateAge(DateTime birthDate) { + DateTime currentDate = DateTime.now(); + var age = AgeCalculator.age(birthDate, today: currentDate); + return age; + } + + dobState.addListener( + () { + // print(calculateAge(dobState.value)); + if (dobRb.value == "date") { + var age = calculateAge(dobState.value); + yearCtr.text = age.years.toString(); + monthCtr.text = age.months.toString(); + dayCtr.text = age.days.toString(); + } + }, + ); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + nameCtr.text = tempPatient.patientData?.name ?? ""; + prefixCtr.text = tempPatient.patientData?.prefix ?? ""; + suffixCtr.text = tempPatient.patientData?.suffix ?? ""; + nikCtr.text = tempPatient.patientData?.nik ?? ""; + nipCtr.text = tempPatient.patientData?.nip ?? ""; + withoutNIK.value = + tempPatient.patientData?.withoutNIK == 'Y' ? true : false; + hpCtr.text = tempPatient.patientData?.hp ?? ""; + addressCtr.text = tempPatient.patientData?.address ?? ""; + diagnosisCtr.text = tempPatient.patientData?.diagnosis ?? ""; + noteCtr.text = tempPatient.patientData?.note ?? ""; + noRmCtr.text = tempPatient.patientData?.noRM ?? ""; + jabatanCtr.text = tempPatient.patientData?.jabatan ?? ""; + kedudukanCtr.text = tempPatient.patientData?.kedudukan ?? ""; + lokasiCtr.text = tempPatient.patientData?.lokasi ?? ""; + pekerjaanCtr.text = tempPatient.patientData?.pekerjaan ?? ""; + // DateFormat('dd-MM-yyyy').format(DateTime.now()) + dobCtr.text = DateFormat('dd-MM-yyyy').format(DateTime.parse( + tempPatient.patientData?.dob ?? DateTime.now().toString())); + dobState.value = DateTime.parse( + tempPatient.patientData?.dob ?? DateTime.now().toString()); + ref + .read(GetFilterRegistrationProvider.notifier) + .getData(token: auth!.token ?? ""); + }); + return () {}; + }, []); + + ref.listen( + GetFilterRegistrationProvider, + (previous, next) { + if (next is GetFilterRegistrationStateInit) { + getFilterLoading.value = true; + } else if (next is GetFilterRegistrationStateLoading) { + getFilterLoading.value = true; + } else if (next is GetFilterRegistrationStateError) { + SanckbarWidget(context, next.message, snackbarType.error); + getFilterLoading.value = false; + Constant.autoLogout(context: context, msg: next.message); + } else if (next is GetFilterRegistrationStateDone) { + // print(jsonEncode(next.model)); + sapaan.value = next.model.titles!; + gender.value = next.model.gender!; + // selectedGender.value = next.model.gender![0]; + if (tempPatient.patientData?.saluation != null) { + var sp = next.model.titles!.firstWhere( + (element) => element.id == tempPatient.patientData?.saluation); + selectedSaluation.value = sp; + } + if (tempPatient.patientData?.gender != null) { + var gd = next.model.gender!.firstWhere( + (element) => element.id == tempPatient.patientData?.gender); + selectedGender.value = gd; + } + sapaanKey.value = sapaanKey.value + 1; + genderKey.value = genderKey.value + 1; + getFilterLoading.value = false; + } + }, + ); + saveEdit() { + if (withoutNIK.value == false) { + if (nikCtr.text.isEmpty) { + SanckbarWidget(context, "NIK Harus diisi", snackbarType.warning); + return; + } + if (nikCtr.text.length < 16 || nikCtr.text.length > 16) { + SanckbarWidget( + context, "NIK berjumlah 16 digit", snackbarType.warning); + return; + } + } + + if (nameCtr.text.isEmpty || + selectedGender.value.id == null || + selectedSaluation.value.id == null || + jabatanCtr.text.isEmpty || + kedudukanCtr.text.isEmpty || + lokasiCtr.text.isEmpty || + pekerjaanCtr.text.isEmpty) { + // print(nameCtr.text); + // print(selectedGender.value.name); + SanckbarWidget( + context, + "Sapaan, Nama, Tanggal Lahir, Jenis Kelamin, jabatan, kedudukan, lokasi, dan pekerjaan Harus Diisi !", + snackbarType.warning); + } else { + PatientData data = PatientData( + noRM: noRmCtr.text, + saluation: selectedSaluation.value.id, + name: nameCtr.text, + prefix: prefixCtr.text, + suffix: suffixCtr.text, + dob: dobState.value.toString(), + nik: nikCtr.text, + nip: nipCtr.text, + withoutNIK: withoutNIK.value ? "Y" : "N", + gender: selectedGender.value.id.toString(), + address: addressCtr.text, + diagnosis: diagnosisCtr.text, + hp: hpCtr.text, + jabatan: jabatanCtr.text, + kedudukan: kedudukanCtr.text, + lokasi: lokasiCtr.text, + pekerjaan: pekerjaanCtr.text, + note: noteCtr.text); + ref.read(registrationEditPatientProvider.notifier).state = + RegistrationModel( + token: auth?.token ?? "", + patientId: tempPatient.patientId, + patientData: data, + ); + print(jsonEncode(RegistrationModel( + token: auth?.token ?? "", + patientId: tempPatient.patientId, + patientData: data, + ))); + ref.read(EditPatientProviderProvider.notifier).editPatent( + prm: RegistrationModel( + token: auth?.token ?? "", + patientId: tempPatient.patientId, + patientData: data, + )); + } + } + + ref.listen( + EditPatientProviderProvider, + (previous, next) { + if (next is EditPatientProviderStateInit) { + editLoading.value = true; + } else if (next is EditPatientProviderStateLoading) { + editLoading.value = true; + } else if (next is EditPatientProviderStateError) { + SanckbarWidget(context, next.message, snackbarType.error); + editLoading.value = false; + Constant.autoLogout(context: context, msg: next.message); + } else if (next is EditPatientProviderStateDone) { + SanckbarWidget( + context, "Berhasil Mengubah Data patient", snackbarType.success); + editLoading.value = false; + Navigator.pop(context, "done"); + } + }, + ); + + return Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 1032), + height: Constant.getActualY(context: context, y: 62), + child: Text('Edit Pasien', + style: Constant.h2_600(context: context) + .copyWith(color: Constant.textBlack)), + ), + SizedBox(height: Constant.getActualY(context: context, y: 40)), + + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 120), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + DropdownMenu( + menuHeight: + Constant.getActualY(context: context, y: 300), + key: ValueKey(sapaanKey.value), + initialSelection: selectedSaluation.value, + trailingIcon: getFilterLoading.value + ? LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20) + : null, + width: Constant.getActualX(context: context, x: 120), + hintText: "Sapaan", + textStyle: Constant.body2_400(context: context), + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + )), + label: const Text( + "Sapaan", + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + onSelected: (value) { + onSelectSaluation(value!); + selectedSaluation.value = value; + }, + dropdownMenuEntries: sapaan.value + .map>( + (e) => DropdownMenuEntry( + value: e, label: e.name ?? "")) + .toList()), + ], + )), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 160), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + hintText: 'Prefix', + labelText: "Prefix", + controller: prefixCtr, + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 520), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + hintText: 'Name', + labelText: "Nama", + controller: nameCtr, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 160), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + hintText: 'Suffix', + labelText: "suffix", + controller: suffixCtr, + ), + ), + ], + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Row( + children: [ + Radio( + value: "date", + groupValue: dobRb.value, + onChanged: (value) { + dobRb.value = value!; + }, + ), + SizedBox(width: Constant.getActualX(context: context, x: 20)), + SizedBox( + width: Constant.getActualX(context: context, x: 252), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + TextField( + enabled: dobRb.value != "age", + controller: dobCtr, + style: Constant.body2_400(context: context), + decoration: InputDecoration( + suffixIcon: Icon(Icons.calendar_today), + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + ), + labelText: "Tanggal Lahir" //label text of field + ), + readOnly: true, + onTap: () async { + DateTime? pickedDate = await showDatePicker( + // locale: Locale("id"), + context: context, + initialDate: dobState.value, + firstDate: DateTime(1800), + initialEntryMode: DatePickerEntryMode.calendarOnly, + + //DateTime.now() - not to allow to choose before today. + lastDate: DateTime(2100)); + + if (pickedDate != null) { + print( + pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000 + String formattedDate = + DateFormat('dd-MM-yyyy').format(pickedDate); + print( + formattedDate); //formatted date output using intl package => 2021-03-16 + dobCtr.text = + formattedDate; //set output date to TextField value. + dobState.value = pickedDate; + } else {} + }, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + Radio( + value: "age", + groupValue: dobRb.value, + onChanged: (value) { + dobRb.value = value!; + }, + ), + SizedBox(width: Constant.getActualX(context: context, x: 20)), + Stack( + clipBehavior: Clip.none, + // alignment: AlignmentDirectional.bottomCenter, + children: [ + Container( + padding: EdgeInsets.symmetric( + horizontal: + Constant.getActualX(context: context, x: 16), + vertical: Constant.getActualY(context: context, y: 12)), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey)), + child: Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 90), + height: Constant.getActualY(context: context, y: 40), + child: CustomTextField( + onChange: (value) { + if (dobRb.value == "age") { + DateTime coba = Jiffy.now() + .subtract( + months: int.parse(monthCtr.text), + years: int.parse(yearCtr.text), + days: int.parse(dayCtr.text)) + .dateTime; + // print(coba); + dobState.value = coba; + dobCtr.text = DateFormat('dd-MM-yyyy') + .format(dobState.value); + } + }, + disable: dobRb.value != "date", + hintText: 'Tahun', + labelText: "", + isDense: true, + suffixIcon: SizedBox( + child: Container( + padding: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 8), + bottom: Constant.getActualY( + context: context, y: 8), + right: Constant.getActualX( + context: context, x: 5), + ), + child: Text( + 'Tahun', + style: Constant.body3_400(context: context) + .copyWith(color: Colors.grey), + ), + ), + ), + style: Constant.body3_400(context: context), + hintStyle: Constant.body3_400(context: context), + labelStyle: Constant.body3_400(context: context), + controller: yearCtr, + inputType: TextInputType.number, + ), + ), + SizedBox( + width: + Constant.getActualX(context: context, x: 16)), + SizedBox( + width: Constant.getActualX(context: context, x: 90), + height: Constant.getActualY(context: context, y: 40), + child: CustomTextField( + onChange: (value) { + if (dobRb.value == "age") { + DateTime currentDate = DateTime.now(); + DateTime coba = Jiffy.now() + .subtract( + months: int.parse(monthCtr.text), + years: int.parse(yearCtr.text), + days: int.parse(dayCtr.text)) + .dateTime; + // print(coba); + + dobState.value = coba; + dobCtr.text = DateFormat('dd-MM-yyyy') + .format(dobState.value); + } + }, + disable: dobRb.value != "date", + hintText: 'Bulan', + labelText: "", + controller: monthCtr, + inputType: TextInputType.number, + isDense: true, + suffixIcon: Container( + child: Container( + padding: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 8), + bottom: Constant.getActualY( + context: context, y: 8), + right: Constant.getActualX( + context: context, x: 5), + ), + child: Text( + 'Bulan', + style: Constant.body3_400(context: context) + .copyWith(color: Colors.grey), + ), + ), + ), + style: Constant.body3_400(context: context), + hintStyle: Constant.body3_400(context: context), + labelStyle: Constant.body3_400(context: context), + ), + ), + SizedBox( + width: + Constant.getActualX(context: context, x: 16)), + SizedBox( + width: Constant.getActualX(context: context, x: 90), + height: Constant.getActualY(context: context, y: 40), + child: CustomTextField( + disable: dobRb.value != "date", + onChange: (value) { + if (dobRb.value == "age") { + if (int.parse(dayCtr.text) <= 31) { + DateTime currentDate = DateTime.now(); + + DateTime coba = Jiffy.now() + .subtract( + months: int.parse(monthCtr.text), + years: int.parse(yearCtr.text), + days: int.parse(dayCtr.text)) + .dateTime; + // print(coba); + + dobState.value = coba; + dobCtr.text = DateFormat('dd-MM-yyyy') + .format(dobState.value); + } else { + dayCtr.text = "31"; + SanckbarWidget( + context, + "hari tidak lebih dari 31", + snackbarType.warning); + } + } + }, + hintText: 'Hari', + labelText: "", + controller: dayCtr, + inputType: TextInputType.number, + isDense: true, + suffixIcon: Container( + child: Container( + padding: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 8), + bottom: Constant.getActualY( + context: context, y: 8), + right: Constant.getActualX( + context: context, x: 5), + ), + child: Text( + 'Hari', + style: Constant.body3_400(context: context) + .copyWith(color: Colors.grey), + ), + ), + ), + style: Constant.body3_400(context: context), + hintStyle: Constant.body3_400(context: context), + labelStyle: Constant.body3_400(context: context), + ), + ), + ], + ), + ), + Positioned( + top: -8, + left: 10, + child: Container( + color: Colors.white, + child: const Text( + "Umur", + style: TextStyle(color: Colors.grey), + ))) + ], + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 300), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + hintText: 'No RM', + labelText: "No RM", + controller: noRmCtr, + ), + ) + ], + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 240), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + inputType: TextInputType.number, + controller: nikCtr, + hintText: withoutNIK.value + ? "Pasien Tanpa NIK Data Historis Tidak Terkonsolidasi" + : 'NIK', + labelText: withoutNIK.value + ? "Pasien Tanpa NIK Data Historis Tidak Terkonsolidasi" + : "NIK"), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 20)), + Center( + child: Transform.scale( + scale: 0.8, + child: Checkbox( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4)), + value: withoutNIK.value, + onChanged: (value) { + withoutNIK.value = value!; + if (value == true) {} + }), + ), + ), + Text('Tanpa NIK', + style: Constant.body2_400(context: context) + .copyWith(color: Constant.textBlack)), + // SizedBox(width: Constant.getActualX(context: context, x: 24)), + // SizedBox( + // width: Constant.getActualX(context: context, x: 250), + // child: DropdownMenu( + // key: ValueKey(genderKey.value), + // initialSelection: selectedGender.value, + // trailingIcon: getFilterLoading.value + // ? LoadingAnimationWidget.discreteCircle( + // color: Constant.primaryBlue, size: 20) + // : null, + // width: Constant.getActualX(context: context, x: 250), + // hintText: "Jenis Kelamin", + // textStyle: Constant.body2_400(context: context), + // inputDecorationTheme: InputDecorationTheme( + // border: OutlineInputBorder( + // borderSide: BorderSide(color: Constant.primaryBlue), + // borderRadius: BorderRadius.circular(8), + // )), + // label: const Text("jenis Kelamin"), + // onSelected: (value) { + // onSelectGender(value!); + // selectedGender.value = value; + // }, + // dropdownMenuEntries: gender.value + // .map>((e) => + // DropdownMenuEntry(value: e, label: e.name ?? "")) + // .toList())), + // SizedBox(width: Constant.getActualX(context: context, x: 24)), + // SizedBox( + // width: Constant.getActualX(context: context, x: 365), + // // height: Constant.getActualY(context: context, y: 56), + // child: CustomTextField( + // hintText: 'No Hp', + // labelText: "No Hp", + // controller: hpCtr, + // ), + // ), + ], + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 304), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + DropdownMenu( + key: ValueKey(genderKey.value), + initialSelection: selectedGender.value, + trailingIcon: getFilterLoading.value + ? LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20) + : null, + width: Constant.getActualX(context: context, x: 304), + hintText: "Jenis Kelamin", + textStyle: Constant.body2_400(context: context), + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + )), + label: const Text("jenis Kelamin"), + onSelected: (value) { + onSelectGender(value!); + selectedGender.value = value; + }, + dropdownMenuEntries: gender.value + .map>( + (e) => DropdownMenuEntry( + value: e, label: e.name ?? "")) + .toList()), + ], + )), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + inputType: TextInputType.number, + hintText: 'No Hp', + labelText: "No Hp", + controller: hpCtr, + ), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + child: CustomTextField( + hintText: "NIP", + labelText: "NIP", + controller: nipCtr, + ), + ), + ], + ), + // SizedBox(height: Constant.getActualY(context: context, y: 24)), + // Row( + // children: [], + // ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 304), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + hintText: 'Jabatan', + labelText: "Jabatan", + controller: jabatanCtr, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + hintText: 'Kedudukan', + labelText: "Kedudukan", + controller: kedudukanCtr, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + hintText: 'Lokasi', + labelText: "Lokasi", + controller: lokasiCtr, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + hintText: 'Pekerjaan', + labelText: "Pekerjaan", + controller: pekerjaanCtr, + ), + ], + ), + ) + ], + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 1032), + // height: Constant.getActualY(context: context, y: 120), + child: CustomTextField( + controller: addressCtr, + hintText: 'Alamat', + labelText: "Alamat", + maxLines: 4, + minLines: 1), + ), + SizedBox(height: Constant.getActualY(context: context, y: 30)), + SizedBox( + width: Constant.getActualX(context: context, x: 1032), + // height: Constant.getActualY(context: context, y: 120), + + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + OutlinedButton( + style: OutlinedButton.styleFrom( + backgroundColor: Colors.white, + side: BorderSide(color: Colors.grey.shade400, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: !editLoading.value + ? () { + ref + .read(registrationEditPatientProvider.notifier) + .state = RegistrationModel(); + + Navigator.pop(context); + } + : null, + child: SizedBox( + height: Constant.getActualY(context: context, y: 56), + child: Center( + child: Text('Batal', + style: Constant.button_medium(context: context) + .copyWith(color: Constant.textBlack)), + ), + )), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Constant.green, + side: BorderSide(color: Constant.green, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + onPressed: !editLoading.value + ? () { + saveEdit(); + } + : null, + child: SizedBox( + height: Constant.getActualY(context: context, y: 56), + child: Center( + child: editLoading.value + ? LoadingAnimationWidget.discreteCircle( + color: Constant.primaryRed, size: 20) + : Text('Simpan', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textWhite)), + ), + )), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/screen/registrasi/dialog_pendaftaran_pasien.dart b/lib/screen/registrasi/dialog_pendaftaran_pasien.dart new file mode 100644 index 0000000..f792377 --- /dev/null +++ b/lib/screen/registrasi/dialog_pendaftaran_pasien.dart @@ -0,0 +1,141 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/app/constant.dart'; + +import 'package:mitra_corporate/screen/registrasi/form_data_pasien.dart'; +import 'package:mitra_corporate/screen/registrasi/form_detail_order.dart'; +import 'package:mitra_corporate/screen/registrasi/form_pemeriksaan.dart'; +import 'package:mitra_corporate/screen/registrasi/registrasi_sukses.dart'; +import 'package:mitra_corporate/widgets/stepper_registrasi.dart'; + +import '../../provider/registrasi_provider.dart'; + +class DialogPendaftaranPasien extends HookConsumerWidget { + const DialogPendaftaranPasien({super.key}); + + // const DialogDataPasien({ + // Key? key, + // }) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final bacaRegistrasiProvider = ref.watch(registrasiProvider); + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: SimpleDialog( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(12))), + contentPadding: const EdgeInsets.all(40.0), + children: [ + Column(children: [ + (bacaRegistrasiProvider == 3) + ? Text('') + : SizedBox( + width: Constant.getActualX(context: context, x: 1080), + height: Constant.getActualY(context: context, y: 62), + child: Text('Pendaftaran Pasien Baru', + style: Constant.h2_600(context: context) + .copyWith(color: Constant.textBlack)), + ), + SizedBox(height: Constant.getActualY(context: context, y: 40)), + //Stepper + (bacaRegistrasiProvider == 3) + ? Text('') + : SizedBox( + width: Constant.getActualX(context: context, x: 1080), + height: Constant.getActualY(context: context, y: 100), + child: StepperRegistrasi()), + SizedBox( + height: Constant.getActualY(context: context, y: 40), + ), + // membaca perubahan value dari registrasi_provider.dart + (bacaRegistrasiProvider == 0) + ? FormDataPasien() + : (bacaRegistrasiProvider == 1) + ? FormPemeriksaan() + : (bacaRegistrasiProvider == 2) + ? DetailOrder() + : (bacaRegistrasiProvider == 3) + ? RegistrasiSukses() + : Text('Tidak ada view'), + + // if(bacaRegistrasiProvider == 1){ + // FormDataPasien() + // } else{ + // if(bacaRegistrasiProvider == 2){ + // Text('2') + // }else{ + // Text('3') + // } + // } + // + // SizedBox(height: Constant.getActualY(context: context, y: 24)), + // Container( + // // height: Constant.getActualY(context: context, y: 100), + // // color: Colors.red, + // padding: const EdgeInsets.only(left: 32, right: 32), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // (bacaRegistrasiProvider == 4) + // ? Text('') + // : OutlinedButton( + // style: OutlinedButton.styleFrom( + // backgroundColor: Colors.white, + // side: BorderSide( + // color: Constant.primaryBlue, width: 2), + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(8))), + // onPressed: () { + // ref + // .read(registrasiProvider.state) + // .update((state) => state - 1); + // }, + // child: SizedBox( + // height: Constant.getActualY(context: context, y: 56), + // child: Center( + // child: Text('Batal', + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textBlue)), + // ), + // )), + // SizedBox(width: Constant.getActualX(context: context, x: 24)), + // (bacaRegistrasiProvider == 4) + // ? Text('') + // : ElevatedButton( + // style: ElevatedButton.styleFrom( + // backgroundColor: Constant.primaryBlue, + // side: + // BorderSide(color: Constant.primaryBlue, width: 2), + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(8), + // ), + // ), + + // // onPressed: () { + // // showDialog( + // // context: context, + // // builder: ((context) => DialogPemeriksaan())); + // // }, + // onPressed: () { + // ref + // .read(registrasiProvider.state) + // .update((state) => state + 1); + // }, + // child: SizedBox( + // height: Constant.getActualY(context: context, y: 56), + // child: Center( + // child: Text('Simpan Perubahan', + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textWhite)), + // ), + // )), + // ], + // ), + // ) + ]) + ]), + ); + } +} diff --git a/lib/screen/registrasi/edit_patient_provider.dart b/lib/screen/registrasi/edit_patient_provider.dart new file mode 100644 index 0000000..c749e40 --- /dev/null +++ b/lib/screen/registrasi/edit_patient_provider.dart @@ -0,0 +1,65 @@ +import 'package:mitra_corporate/repository/patient_repository.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/registration_model.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class EditPatientProviderState extends Equatable { + final DateTime date; + const EditPatientProviderState(this.date); + @override + List get props => [date]; +} + +class EditPatientProviderStateInit extends EditPatientProviderState { + EditPatientProviderStateInit() : super(DateTime.now()); +} + +class EditPatientProviderStateLoading extends EditPatientProviderState { + EditPatientProviderStateLoading() : super(DateTime.now()); +} + +class EditPatientProviderStateError extends EditPatientProviderState { + final String message; + EditPatientProviderStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class EditPatientProviderStateDone extends EditPatientProviderState { + final String msg; + EditPatientProviderStateDone({ + required this.msg, + }) : super(DateTime.now()); +} + +//notifier +class EditPatientProviderNotifier + extends StateNotifier { + final Ref ref; + EditPatientProviderNotifier({ + required this.ref, + }) : super(EditPatientProviderStateInit()); + + void editPatent({required RegistrationModel prm}) async { + try { + state = EditPatientProviderStateLoading(); + final dio = ref.read(dioProvider); + final resp = await PatientRepository(dio: dio).editPatient(prm: prm); + state = EditPatientProviderStateDone(msg: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = EditPatientProviderStateError(message: e.message.toString()); + } else { + state = EditPatientProviderStateError(message: e.toString()); + } + } + } +} + +//provider +final EditPatientProviderProvider = StateNotifierProvider< + EditPatientProviderNotifier, + EditPatientProviderState>((ref) => EditPatientProviderNotifier(ref: ref)); diff --git a/lib/screen/registrasi/form_data_pasien.dart b/lib/screen/registrasi/form_data_pasien.dart new file mode 100644 index 0000000..0ea454c --- /dev/null +++ b/lib/screen/registrasi/form_data_pasien.dart @@ -0,0 +1,931 @@ +import 'package:age_calculator/age_calculator.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/intl.dart'; +import 'package:jiffy/jiffy.dart'; +import 'package:mitra_corporate/app/constant.dart'; +import 'package:mitra_corporate/model/registration_model.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/screen/registrasi/get_filter_provider.dart'; +import 'package:mitra_corporate/widgets/custom_snackbar_widget.dart'; +import 'package:mitra_corporate/widgets/custom_text_field.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../provider/registrasi_provider.dart'; + +class FormDataPasien extends HookConsumerWidget { + const FormDataPasien({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final tempPatient = ref.watch(registrationDataProvider); + final auth = ref.watch(authProvider); + //withoutnik apabila tanpa nik maka true + final withoutNIK = useState(false); + final getFilterLoading = useState(false); + final sapaanKey = useState(1); + final genderKey = useState(1000); + final prefixCtr = useTextEditingController(text: ""); + final nameCtr = useTextEditingController(text: ""); + final suffixCtr = useTextEditingController(text: ""); + final nikCtr = useTextEditingController(text: ""); + final nipCtr = useTextEditingController(text: ""); + final hpCtr = useTextEditingController(text: ""); + final noRmCtr = useTextEditingController(text: ""); + final addressCtr = useTextEditingController(text: ""); + final diagnosisCtr = useTextEditingController(text: ""); + final noteCtr = useTextEditingController(text: ""); + final jabatanCtr = useTextEditingController(text: ""); + final kedudukanCtr = useTextEditingController(text: ""); + final lokasiCtr = useTextEditingController(text: ""); + final pekerjaanCtr = useTextEditingController(text: ""); + final sapaan = useState>(List.empty()); + final selectedSaluation = useState(CustomDropDownModel()); + final gender = useState>(List.empty()); + final selectedGender = useState(CustomDropDownModel()); + + //select salah satu sapaan + onSelectSaluation(CustomDropDownModel value) { + selectedSaluation.value = value; + selectedGender.value = + gender.value.firstWhere((element) => element.id == value.type); + + genderKey.value = genderKey.value + 1; + // sapaanKey.value = sapaanKey.value + 1; + } + + //update gender saat sapaan dipilih + selectedSaluation.addListener(() { + selectedGender.value = gender.value + .firstWhere((element) => element.id == selectedSaluation.value.type); + genderKey.value = genderKey.value + 1; + }); + + //select gender + onSelectGender(CustomDropDownModel value) { + selectedGender.value = value; + } + + final dobRb = useState('date'); + final dobCtr = useTextEditingController( + text: DateFormat('dd-MM-yyyy').format(DateTime.now())); + final dobState = useState(DateTime.now()); + + final yearCtr = useTextEditingController(text: "0"); + final monthCtr = useTextEditingController(text: "0"); + final dayCtr = useTextEditingController(text: "0"); + + //perhitungan umur dari tanggal ke angka + DateDuration calculateAge(DateTime birthDate) { + DateTime currentDate = DateTime.now(); + var age = AgeCalculator.age(birthDate, today: currentDate); + return age; + } + + dobState.addListener( + () { + // print(calculateAge(dobState.value)); + if (dobRb.value == "date") { + var age = calculateAge(dobState.value); + yearCtr.text = age.years.toString(); + monthCtr.text = age.months.toString(); + dayCtr.text = age.days.toString(); + } + }, + ); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + nameCtr.text = tempPatient.patientData?.name ?? ""; + prefixCtr.text = tempPatient.patientData?.prefix ?? ""; + suffixCtr.text = tempPatient.patientData?.suffix ?? ""; + nikCtr.text = tempPatient.patientData?.nik ?? ""; + nipCtr.text = tempPatient.patientData?.nip ?? ""; + withoutNIK.value = + tempPatient.patientData?.withoutNIK == 'Y' ? true : false; + hpCtr.text = tempPatient.patientData?.hp ?? ""; + addressCtr.text = tempPatient.patientData?.address ?? ""; + diagnosisCtr.text = tempPatient.patientData?.diagnosis ?? ""; + noteCtr.text = tempPatient.patientData?.note ?? ""; + noRmCtr.text = tempPatient.patientData?.noRM ?? ""; + jabatanCtr.text = tempPatient.patientData?.jabatan ?? ""; + kedudukanCtr.text = tempPatient.patientData?.kedudukan ?? ""; + lokasiCtr.text = tempPatient.patientData?.lokasi ?? ""; + pekerjaanCtr.text = tempPatient.patientData?.pekerjaan ?? ""; + // DateFormat('dd-MM-yyyy').format(DateTime.now()) + dobCtr.text = DateFormat('dd-MM-yyyy').format(DateTime.parse( + tempPatient.patientData?.dob ?? DateTime.now().toString())); + dobState.value = DateTime.parse( + tempPatient.patientData?.dob ?? DateTime.now().toString()); + ref + .read(GetFilterRegistrationProvider.notifier) + .getData(token: auth!.token ?? ""); + }); + return () {}; + }, []); + + ref.listen( + GetFilterRegistrationProvider, + (previous, next) { + if (next is GetFilterRegistrationStateInit) { + getFilterLoading.value = true; + } else if (next is GetFilterRegistrationStateLoading) { + getFilterLoading.value = true; + } else if (next is GetFilterRegistrationStateError) { + SanckbarWidget(context, next.message, snackbarType.error); + getFilterLoading.value = false; + Constant.autoLogout(context: context, msg: next.message); + } else if (next is GetFilterRegistrationStateDone) { + // print(jsonEncode(next.model)); + sapaan.value = next.model.titles!; + gender.value = next.model.gender!; + // selectedGender.value = next.model.gender![0]; + if (tempPatient.patientData?.saluation != null) { + var sp = next.model.titles!.firstWhere( + (element) => element.id == tempPatient.patientData?.saluation); + selectedSaluation.value = sp; + } + if (tempPatient.patientData?.gender != null) { + var gd = next.model.gender!.firstWhere( + (element) => element.id == tempPatient.patientData?.gender); + selectedGender.value = gd; + } + sapaanKey.value = sapaanKey.value + 1; + genderKey.value = genderKey.value + 1; + getFilterLoading.value = false; + } + }, + ); + + //Function ke step selanjutny(pilih pemeriksaan) + nextStep() { + if (withoutNIK.value == false) { + if (nikCtr.text.isEmpty) { + SanckbarWidget(context, "NIK Harus diisi", snackbarType.warning); + return; + } + if (nikCtr.text.length < 16 || nikCtr.text.length > 16) { + SanckbarWidget( + context, "NIK berjumlah 16 digit", snackbarType.warning); + return; + } + } + + if (nameCtr.text.isEmpty || + selectedGender.value.id == null || + selectedSaluation.value.id == null || + jabatanCtr.text.isEmpty || + kedudukanCtr.text.isEmpty || + lokasiCtr.text.isEmpty || + pekerjaanCtr.text.isEmpty) { + // print(nameCtr.text); + // print(selectedGender.value.name); + SanckbarWidget( + context, + "Sapaan, Nama, Tanggal Lahir, Jenis Kelamin, jabatan, kedudukan, lokasi, dan pekerjaan Harus Diisi !", + snackbarType.warning); + } else { + var temp = ref.watch(registrationDataProvider); + PatientData data = PatientData( + noRM: noRmCtr.text, + saluation: selectedSaluation.value.id, + name: nameCtr.text, + prefix: prefixCtr.text, + suffix: suffixCtr.text, + dob: dobState.value.toString(), + nik: nikCtr.text, + nip: nipCtr.text, + withoutNIK: withoutNIK.value ? "Y" : "N", + gender: selectedGender.value.id.toString(), + address: addressCtr.text, + diagnosis: diagnosisCtr.text, + hp: hpCtr.text, + jabatan: jabatanCtr.text, + kedudukan: kedudukanCtr.text, + lokasi: lokasiCtr.text, + pekerjaan: pekerjaanCtr.text, + note: noteCtr.text); + ref.read(registrationDataProvider.notifier).state = RegistrationModel( + orderID: temp.orderID, + token: auth?.token ?? "", + patientId: temp.patientId ?? "new", + patientData: data, + specimens: temp.specimens, + paket: temp.paket, + bahan: temp.bahan, + tests: temp.tests, + total: temp.total); + ref.read(registrasiProvider.state).update((state) => 1); + } + } + + return Padding( + padding: const EdgeInsets.only(top: 20, bottom: 20, left: 24, right: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 120), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + DropdownMenu( + menuHeight: + Constant.getActualY(context: context, y: 300), + key: ValueKey(sapaanKey.value), + initialSelection: selectedSaluation.value, + trailingIcon: getFilterLoading.value + ? LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20) + : null, + width: Constant.getActualX(context: context, x: 120), + hintText: "Sapaan", + textStyle: Constant.body2_400(context: context), + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + )), + label: const Text( + "Sapaan", + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + onSelected: (value) { + onSelectSaluation(value!); + selectedSaluation.value = value; + }, + dropdownMenuEntries: sapaan.value + .map>( + (e) => DropdownMenuEntry( + value: e, label: e.name ?? "")) + .toList()), + ], + )), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 160), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + hintText: 'Prefix', + labelText: "Prefix", + controller: prefixCtr, + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 520), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + hintText: 'Name', + labelText: "Nama", + controller: nameCtr, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 160), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + hintText: 'Suffix', + labelText: "suffix", + controller: suffixCtr, + ), + ), + ], + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Row( + children: [ + Radio( + value: "date", + groupValue: dobRb.value, + onChanged: (value) { + dobRb.value = value!; + }, + ), + SizedBox(width: Constant.getActualX(context: context, x: 20)), + SizedBox( + width: Constant.getActualX(context: context, x: 252), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + TextField( + enabled: dobRb.value != "age", + controller: dobCtr, + style: Constant.body2_400(context: context), + decoration: InputDecoration( + suffixIcon: Icon(Icons.calendar_today), + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + ), + labelText: "Tanggal Lahir" //label text of field + ), + readOnly: true, + onTap: () async { + DateTime? pickedDate = await showDatePicker( + // locale: Locale("id"), + context: context, + initialDate: dobState.value, + firstDate: DateTime(1800), + initialEntryMode: DatePickerEntryMode.calendarOnly, + + //DateTime.now() - not to allow to choose before today. + lastDate: DateTime(2100)); + + if (pickedDate != null) { + print( + pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000 + String formattedDate = + DateFormat('dd-MM-yyyy').format(pickedDate); + print( + formattedDate); //formatted date output using intl package => 2021-03-16 + dobCtr.text = + formattedDate; //set output date to TextField value. + dobState.value = pickedDate; + } else {} + }, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + Radio( + value: "age", + groupValue: dobRb.value, + onChanged: (value) { + dobRb.value = value!; + }, + ), + SizedBox(width: Constant.getActualX(context: context, x: 20)), + Stack( + clipBehavior: Clip.none, + // alignment: AlignmentDirectional.bottomCenter, + children: [ + Container( + padding: EdgeInsets.symmetric( + horizontal: + Constant.getActualX(context: context, x: 16), + vertical: Constant.getActualY(context: context, y: 12)), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey)), + child: Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 90), + height: Constant.getActualY(context: context, y: 40), + child: CustomTextField( + onChange: (value) { + if (dobRb.value == "age") { + DateTime coba = Jiffy.now() + .subtract( + months: int.parse(monthCtr.text), + years: int.parse(yearCtr.text), + days: int.parse(dayCtr.text)) + .dateTime; + // print(coba); + dobState.value = coba; + dobCtr.text = DateFormat('dd-MM-yyyy') + .format(dobState.value); + } + }, + disable: dobRb.value != "date", + hintText: 'Tahun', + labelText: "", + isDense: true, + suffixIcon: SizedBox( + child: Container( + padding: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 8), + bottom: Constant.getActualY( + context: context, y: 8), + right: Constant.getActualX( + context: context, x: 5), + ), + child: Text( + 'Tahun', + style: Constant.body3_400(context: context) + .copyWith(color: Colors.grey), + ), + ), + ), + style: Constant.body3_400(context: context), + hintStyle: Constant.body3_400(context: context), + labelStyle: Constant.body3_400(context: context), + controller: yearCtr, + inputType: TextInputType.number, + ), + ), + SizedBox( + width: + Constant.getActualX(context: context, x: 16)), + SizedBox( + width: Constant.getActualX(context: context, x: 90), + height: Constant.getActualY(context: context, y: 40), + child: CustomTextField( + onChange: (value) { + if (dobRb.value == "age") { + DateTime currentDate = DateTime.now(); + DateTime coba = Jiffy.now() + .subtract( + months: int.parse(monthCtr.text), + years: int.parse(yearCtr.text), + days: int.parse(dayCtr.text)) + .dateTime; + // print(coba); + + dobState.value = coba; + dobCtr.text = DateFormat('dd-MM-yyyy') + .format(dobState.value); + } + }, + disable: dobRb.value != "date", + hintText: 'Bulan', + labelText: "", + controller: monthCtr, + inputType: TextInputType.number, + isDense: true, + suffixIcon: Container( + child: Container( + padding: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 8), + bottom: Constant.getActualY( + context: context, y: 8), + right: Constant.getActualX( + context: context, x: 5), + ), + child: Text( + 'Bulan', + style: Constant.body3_400(context: context) + .copyWith(color: Colors.grey), + ), + ), + ), + style: Constant.body3_400(context: context), + hintStyle: Constant.body3_400(context: context), + labelStyle: Constant.body3_400(context: context), + ), + ), + SizedBox( + width: + Constant.getActualX(context: context, x: 16)), + SizedBox( + width: Constant.getActualX(context: context, x: 90), + height: Constant.getActualY(context: context, y: 40), + child: CustomTextField( + disable: dobRb.value != "date", + onChange: (value) { + if (dobRb.value == "age") { + if (int.parse(dayCtr.text) <= 31) { + DateTime currentDate = DateTime.now(); + + DateTime coba = Jiffy.now() + .subtract( + months: int.parse(monthCtr.text), + years: int.parse(yearCtr.text), + days: int.parse(dayCtr.text)) + .dateTime; + // print(coba); + + dobState.value = coba; + dobCtr.text = DateFormat('dd-MM-yyyy') + .format(dobState.value); + } else { + dayCtr.text = "31"; + SanckbarWidget( + context, + "hari tidak lebih dari 31", + snackbarType.warning); + } + } + }, + hintText: 'Hari', + labelText: "", + controller: dayCtr, + inputType: TextInputType.number, + isDense: true, + suffixIcon: Container( + child: Container( + padding: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 8), + bottom: Constant.getActualY( + context: context, y: 8), + right: Constant.getActualX( + context: context, x: 5), + ), + child: Text( + 'Hari', + style: Constant.body3_400(context: context) + .copyWith(color: Colors.grey), + ), + ), + ), + style: Constant.body3_400(context: context), + hintStyle: Constant.body3_400(context: context), + labelStyle: Constant.body3_400(context: context), + ), + ), + ], + ), + ), + Positioned( + top: -8, + left: 10, + child: Container( + color: Colors.white, + child: const Text( + "Umur", + style: TextStyle(color: Colors.grey), + ))) + ], + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 300), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + hintText: 'No RM', + labelText: "No RM", + controller: noRmCtr, + ), + ) + ], + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 240), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + inputType: TextInputType.number, + controller: nikCtr, + hintText: withoutNIK.value + ? "Pasien Tanpa NIK Data Historis Tidak Terkonsolidasi" + : 'NIK', + labelText: withoutNIK.value + ? "Pasien Tanpa NIK Data Historis Tidak Terkonsolidasi" + : "NIK"), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 20)), + Center( + child: Transform.scale( + scale: 0.8, + child: Checkbox( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4)), + value: withoutNIK.value, + onChanged: (value) { + withoutNIK.value = value!; + if (value == true) {} + }), + ), + ), + Text('Tanpa NIK', + style: Constant.body2_400(context: context) + .copyWith(color: Constant.textBlack)), + // SizedBox(width: Constant.getActualX(context: context, x: 24)), + // SizedBox( + // width: Constant.getActualX(context: context, x: 250), + // child: DropdownMenu( + // key: ValueKey(genderKey.value), + // initialSelection: selectedGender.value, + // trailingIcon: getFilterLoading.value + // ? LoadingAnimationWidget.discreteCircle( + // color: Constant.primaryBlue, size: 20) + // : null, + // width: Constant.getActualX(context: context, x: 250), + // hintText: "Jenis Kelamin", + // textStyle: Constant.body2_400(context: context), + // inputDecorationTheme: InputDecorationTheme( + // border: OutlineInputBorder( + // borderSide: BorderSide(color: Constant.primaryBlue), + // borderRadius: BorderRadius.circular(8), + // )), + // label: const Text("jenis Kelamin"), + // onSelected: (value) { + // onSelectGender(value!); + // selectedGender.value = value; + // }, + // dropdownMenuEntries: gender.value + // .map>((e) => + // DropdownMenuEntry(value: e, label: e.name ?? "")) + // .toList())), + // SizedBox(width: Constant.getActualX(context: context, x: 24)), + // SizedBox( + // width: Constant.getActualX(context: context, x: 365), + // // height: Constant.getActualY(context: context, y: 56), + // child: CustomTextField( + // hintText: 'No Hp', + // labelText: "No Hp", + // controller: hpCtr, + // ), + // ), + ], + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 304), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + DropdownMenu( + key: ValueKey(genderKey.value), + initialSelection: selectedGender.value, + trailingIcon: getFilterLoading.value + ? LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20) + : null, + width: Constant.getActualX(context: context, x: 304), + hintText: "Jenis Kelamin", + textStyle: Constant.body2_400(context: context), + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + )), + label: const Text("jenis Kelamin"), + onSelected: (value) { + onSelectGender(value!); + selectedGender.value = value; + }, + dropdownMenuEntries: gender.value + .map>( + (e) => DropdownMenuEntry( + value: e, label: e.name ?? "")) + .toList()), + ], + )), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + inputType: TextInputType.number, + hintText: 'No Hp', + labelText: "No Hp", + controller: hpCtr, + ), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + child: CustomTextField( + hintText: "NIP", + labelText: "NIP", + controller: nipCtr, + ), + ), + ], + ), + // SizedBox(height: Constant.getActualY(context: context, y: 24)), + // Row( + // children: [], + // ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 304), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + hintText: 'Jabatan', + labelText: "Jabatan", + controller: jabatanCtr, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + hintText: 'Kedudukan', + labelText: "Kedudukan", + controller: kedudukanCtr, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + hintText: 'Lokasi', + labelText: "Lokasi", + controller: lokasiCtr, + ), + ], + ), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 218), + // height: Constant.getActualY(context: context, y: 56), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Text( + "*", + style: Constant.body2_400(context: context) + .copyWith(color: Constant.primaryRed), + ), + ), + CustomTextField( + hintText: 'Pekerjaan', + labelText: "Pekerjaan", + controller: pekerjaanCtr, + ), + ], + ), + ) + ], + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 1032), + // height: Constant.getActualY(context: context, y: 120), + child: CustomTextField( + controller: addressCtr, + hintText: 'Alamat', + labelText: "Alamat", + maxLines: 4, + minLines: 1), + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 1032), + // height: Constant.getActualY(context: context, y: 120), + child: CustomTextField( + controller: diagnosisCtr, + hintText: 'Diagnosis', + labelText: "Diagnosis", + maxLines: 4, + minLines: 1), + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + SizedBox( + width: Constant.getActualX(context: context, x: 1032), + // height: Constant.getActualY(context: context, y: 120), + child: CustomTextField( + controller: noteCtr, + hintText: 'Catatan FO', + labelText: "Catatan FO", + maxLines: 4, + minLines: 1), + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + SizedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + OutlinedButton( + style: OutlinedButton.styleFrom( + backgroundColor: Colors.white, + side: BorderSide(color: Colors.grey.shade400, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: () { + ref.read(registrationDataProvider.notifier).state = + RegistrationModel(); + ref.read(selectedTestProvider.notifier).state = + List.empty(growable: true); + ref.read(selectedPacketProvider.notifier).state = + List.empty(growable: true); + Navigator.pop(context); + }, + child: SizedBox( + height: Constant.getActualY(context: context, y: 56), + child: Center( + child: Text('Batal', + style: Constant.button_medium(context: context) + .copyWith(color: Constant.textBlack)), + ), + )), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Constant.green, + side: BorderSide(color: Constant.green, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + onPressed: () { + nextStep(); + }, + child: SizedBox( + height: Constant.getActualY(context: context, y: 56), + child: Center( + child: Text('Berikutnya', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textWhite)), + ), + )), + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/screen/registrasi/form_detail_order.dart b/lib/screen/registrasi/form_detail_order.dart new file mode 100644 index 0000000..5dacfde --- /dev/null +++ b/lib/screen/registrasi/form_detail_order.dart @@ -0,0 +1,631 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; + +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:jiffy/jiffy.dart'; + +import 'package:mitra_corporate/model/registration_model.dart'; +import 'package:mitra_corporate/model/test_model.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/provider/registrasi_provider.dart'; +import 'package:mitra_corporate/screen/registrasi/get_specimen_provider.dart'; +import 'package:mitra_corporate/screen/registrasi/new_order_provider.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../widgets/custom_snackbar_widget.dart'; +import '../../widgets/tests_tabel_widget.dart'; + +class DetailOrder extends HookConsumerWidget { + const DetailOrder({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final auth = ref.watch(authProvider); + final tempRegistrationData = ref.watch(registrationDataProvider); + final testListAll = ref.read(tempTestListProvider); + final selectedTest = ref.read(selectedTestProvider); + final dialogAction = ref.watch(dialogOrderActionProvider); + + final testList = useState>(List.empty()); + final testScrollCtr = useScrollController(); + final specimenLoading = useState(false); + final addOrderLoading = ref.watch(addOrderLoadingProvider); + final tabctr = useTabController(initialLength: 2); + final selectedTab = useState(0); + final dateValidation = useState(false); + + dateVal(bool value) { + dateValidation.value = value; + } + + //get specimen & bahan from list of tests + getSpecimen() { + List> test = List.empty(growable: true); + tempRegistrationData.tests?.forEach((element) { + test.add({ + "id": element.id ?? "0", + "tab": element.tab ?? "0", + "sasCode": element.sasCode ?? "-" + }); + }); + // print(jsonEncode(test)); + ref + .read(GetSpecimenProvider.notifier) + .getData(arrTest: test, token: auth?.token ?? "0"); + } + + ref.listen( + GetSpecimenProvider, + (pref, next) { + if (next is GetSpecimenStateInit) { + specimenLoading.value = true; + } else if (next is GetSpecimenStateLoading) { + specimenLoading.value = true; + } else if (next is GetSpecimenStateError) { + specimenLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is GetSpecimenStateDone) { + var tests = tempRegistrationData.tests; + //assign specimn & bahan + List nw = next.model; + List sp = List.empty(growable: true); + List bhn = List.empty(growable: true); + + for (var i = 0; i < tests!.length; i++) { + var tes = tests[i]; + var cek = "${tes.tab}|${tes.id}"; + for (var j = 0; j < nw.length; j++) { + var cek2 = "${nw[j].tab}|${nw[j].id}"; + if (cek == cek2) { + tests[i].specimen = nw[j].specimen?.toSet().toList(); + tests[i].bahan = nw[j].bahan?.toSet().toList(); + + nw[j].specimen?.forEach((element) { + var a = sp.firstWhere( + (dt) => dt.id == element.id, + orElse: () => + Specimens(ctr: TextEditingController(), id: "new"), + ); + if (a.id == "new") { + sp.add(element); + } + }); + nw[j].bahan?.forEach((element) { + var a = bhn.firstWhere( + (dt) => dt.id == element.id, + orElse: () => + Bahan(ctr: TextEditingController(), id: "new"), + ); + if (a.id == "new") { + bhn.add(element); + } + }); + } + } + } + + ref.read(registrationDataProvider.notifier).state = RegistrationModel( + orderID: tempRegistrationData.orderID, + patientId: tempRegistrationData.patientId, + token: auth?.token, + patientData: tempRegistrationData.patientData, + paket: tempRegistrationData.paket, + tests: tests, + specimens: sp.toSet().toList(), + bahan: bhn.toSet().toList(), + total: tempRegistrationData.total); + specimenLoading.value = false; + } + }, + ); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + List temp = List.empty(growable: true); + for (var element in testListAll) { + // "$idTab|$idTest" + for (var i in element.items!) { + String cek = "${element.tabId}|${i.testID}"; + String ck = selectedTest.firstWhere( + (j) => j == cek, + orElse: () => "-1", + ); + if (ck != "-1") { + temp.add(i); + } + } + } + testList.value = temp; + + getSpecimen(); + }); + return () {}; + }, []); + + //delete tests & auto cek & delete specimen/bahan + delete(String idTest, String idtab) { + List temp = tempRegistrationData.tests ?? []; + List selectedTest = ref.watch(selectedTestProvider); + String cek = "$idtab|$idTest"; + double total = double.parse(tempRegistrationData.total!); + + List oldSp = tempRegistrationData.specimens ?? []; + List oldBhn = tempRegistrationData.bahan ?? []; + + Tests removed = tempRegistrationData.tests!.firstWhere( + (element) => element.id == idTest && element.tab == idtab); + temp.removeWhere( + (element) => element.id == idTest && element.tab == idtab); + selectedTest.removeWhere((element) => element == cek); + total = total - double.parse(removed.price!); + // var tests = tempRegistrationData.tests; + + // List nw = next.model; + List newSp = List.empty(growable: true); + List newBhn = List.empty(growable: true); + + for (var j = 0; j < temp.length; j++) { + temp[j].specimen?.forEach((element) { + var a = newSp.firstWhere( + (dt) => dt.id == element.id, + orElse: () => Specimens(ctr: TextEditingController(), id: "new"), + ); + if (a.id == "new") { + newSp.add(element); + } + }); + temp[j].bahan?.forEach((element) { + var a = newBhn.firstWhere( + (dt) => dt.id == element.id, + orElse: () => Bahan(ctr: TextEditingController(), id: "new"), + ); + if (a.id == "new") { + newBhn.add(element); + } + }); + } + // print("old sp"); + // print(jsonEncode(oldSp)); + // print("new sp"); + // print(jsonEncode(newSp)); + //compare old dp and new sp to get deleted specimen + List deletedSp = List.empty(growable: true); + for (var i = 0; i < oldSp.length; i++) { + Specimens sp = newSp.firstWhere((element) => element.id == oldSp[i].id, + orElse: () => + Specimens(ctr: TextEditingController(), id: "deleted")); + if (sp.id == "deleted") { + deletedSp.add(oldSp[i]); + } + } + List tempSp = tempRegistrationData.specimens ?? []; + for (var e in deletedSp) { + tempSp.removeWhere((element) => element.id == e.id); + } + // print("deleted sp"); + // print(jsonEncode(deletedSp)); + // print("old bhn"); + // print(jsonEncode(oldBhn)); + // print("new bhn"); + // print(jsonEncode(newBhn)); + //compare old bhn and new bhn to get deleted bahan + List deletedBhn = List.empty(growable: true); + for (var i = 0; i < oldBhn.length; i++) { + Bahan sp = newBhn.firstWhere((element) => element.id == oldBhn[i].id, + orElse: () => Bahan(ctr: TextEditingController(), id: "deleted")); + if (sp.id == "deleted") { + deletedBhn.add(oldBhn[i]); + } + } + List tempBhn = tempRegistrationData.bahan ?? []; + for (var e in deletedBhn) { + tempBhn.removeWhere((element) => element.id == e.id); + } + // print("deleted bahan"); + // print(jsonEncode(deletedBhn)); + + ref.read(registrationDataProvider.notifier).state = RegistrationModel( + orderID: tempRegistrationData.orderID, + patientId: tempRegistrationData.patientId, + token: auth?.token, + patientData: tempRegistrationData.patientData, + specimens: tempSp, + paket: tempRegistrationData.paket, + bahan: tempBhn, + tests: temp, + total: total.toString()); + ref.read(selectedTestProvider.notifier).state = selectedTest; + if (selectedTest.isEmpty) { + ref.read(registrasiProvider.state).update((state) => state - 1); + } + } + + deletePaket(String idPaket) { + List temp = tempRegistrationData.paket ?? []; + List selectedPaket = ref.watch(selectedPacketProvider); + + selectedPaket.removeWhere((element) => element == idPaket); + temp.removeWhere((element) => element.id == idPaket); + + ref.read(selectedPacketProvider.notifier).state = selectedPaket; + ref.read(registrationDataProvider.notifier).state = RegistrationModel( + orderID: tempRegistrationData.orderID, + patientId: tempRegistrationData.patientId, + token: auth?.token, + patientData: tempRegistrationData.patientData, + specimens: tempRegistrationData.specimens, + paket: temp, + bahan: tempRegistrationData.bahan, + tests: tempRegistrationData.tests, + total: tempRegistrationData.total); + } + + Future pickDate(DateTime initialdate) => showDatePicker( + // locale: Locale("id", "ID"), + context: context, + initialEntryMode: DatePickerEntryMode.calendarOnly, + cancelText: "Batal", + confirmText: "Simpan", + helpText: "Pilih Tanggal", + initialDate: initialdate, + firstDate: DateTime(1800), + lastDate: DateTime(2100)); + + Future pickTime(TimeOfDay initialTime) => showTimePicker( + context: context, + initialTime: initialTime, + cancelText: "Batal", + confirmText: "Simpan", + helpText: "Masukkan Jam dan Menit", + hourLabelText: "Jam", + minuteLabelText: "Menit", + builder: (context, childWidget) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + // Using 24-Hour format + alwaysUse24HourFormat: true), + // If you want 12-Hour format, just change alwaysUse24HourFormat to false or remove all the builder argument + child: childWidget!); + }, + initialEntryMode: TimePickerEntryMode.input); + + Future pickDateTime(String idtab, String idtest, String dt) async { + print(dt); + + // DateTime selectedDate = DateTime.parse(dt); + // DateTime? date = await pickDate(selectedDate); + // if (date == null) return; + + // TimeOfDay? time = await pickTime( + // TimeOfDay(hour: selectedDate.hour, minute: selectedDate.minute)); + // if (time == null) return; + var test = tempRegistrationData.tests ?? []; + for (var i = 0; i < test.length; i++) { + if (test[i].id == idtest && test[i].tab == idtab) { + test[i].date = dt; + } + } + ref.read(registrationDataProvider.notifier).state = RegistrationModel( + orderID: tempRegistrationData.orderID, + patientData: tempRegistrationData.patientData, + specimens: tempRegistrationData.specimens, + tests: test, + token: auth?.token, + patientId: tempRegistrationData.patientId, + bahan: tempRegistrationData.bahan, + total: tempRegistrationData.total); + } + + Future dateValidationFunc( + String idtab, String idtest, bool validate) async { + // DateTime selectedDate = DateTime.parse(dt); + // DateTime? date = await pickDate(selectedDate); + // if (date == null) return; + + // TimeOfDay? time = await pickTime( + // TimeOfDay(hour: selectedDate.hour, minute: selectedDate.minute)); + // if (time == null) return; + var test = tempRegistrationData.tests ?? []; + for (var i = 0; i < test.length; i++) { + if (test[i].id == idtest && test[i].tab == idtab) { + test[i].dateVal = validate; + } + } + ref.read(registrationDataProvider.notifier).state = RegistrationModel( + orderID: tempRegistrationData.orderID, + patientData: tempRegistrationData.patientData, + specimens: tempRegistrationData.specimens, + tests: test, + token: auth?.token, + patientId: tempRegistrationData.patientId, + bahan: tempRegistrationData.bahan, + total: tempRegistrationData.total); + } + + bool validateDate(String? value) { + if (value == null || value.isEmpty) { + SanckbarWidget(context, "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + final splitted = value.split(" "); + if (splitted.length == 2) { + final date = splitted[0]; + final time = splitted[1]; + final splittedDate = date.split("-"); + final splittedTime = time.split(":"); + if (splittedDate.length == 3) { + final day = int.tryParse(splittedDate[0]); + final month = int.tryParse(splittedDate[1]); + final year = int.tryParse(splittedDate[2]); + if (day != null) { + if (day > 31) { + SanckbarWidget( + context, + "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + } + if (month != null) { + if (month > 12) { + SanckbarWidget( + context, + "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + } + // if (year == null) { + // return "Format salah"; + // } + } else { + SanckbarWidget(context, "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + if (splittedTime.length == 2) { + var hour = int.tryParse(splittedTime[0]); + var sec = int.tryParse(splittedTime[1]); + if (hour != null) { + if (hour > 24) { + SanckbarWidget( + context, + "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + } + if (sec != null) { + if (sec > 60) { + SanckbarWidget( + context, + "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + } + } else { + SanckbarWidget(context, "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + } else { + SanckbarWidget(context, "Format Waktu Pengambilan Sample Salah !", + snackbarType.warning); + return true; + } + return false; + } + + addOrder() { + print(jsonEncode(tempRegistrationData)); + for (var element in tempRegistrationData.tests!) { + if (element.date == null || element.date!.isEmpty) { + SanckbarWidget( + context, + "Waktu pengambilan sample ${element.name} Tidak boleh kosong", + snackbarType.warning); + continue; + } + if (validateDate(element.date)) { + continue; + } + + var coba = Jiffy.parse(element.date!, pattern: 'dd-MM-yyyy HH:mm') + .format(pattern: 'yyyy-MM-dd HH:mm'); + print(coba); + } + // print(jsonEncode(tempRegistrationData)); + // return; + ref + .read(NewOrderProviderProvider.notifier) + .addOrder(prm: tempRegistrationData); + } + + ref.listen( + NewOrderProviderProvider, + (pref, next) { + if (next is NewOrderProviderStateInit) { + ref.read(addOrderLoadingProvider.notifier).state = true; + } else if (next is NewOrderProviderStateLoading) { + ref.read(addOrderLoadingProvider.notifier).state = true; + } else if (next is NewOrderProviderStateError) { + ref.read(addOrderLoadingProvider.notifier).state = false; + + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is NewOrderProviderStateDone) { + ref.read(orderNumberProvider.notifier).state = next.number; + print(next.number); + SanckbarWidget( + context, + "Registrasi Berhasil. Order Number ${next.number}", + snackbarType.success); + + ref.read(addOrderLoadingProvider.notifier).state = false; + + ref.read(registrasiProvider.state).update((state) => 3); + print(jsonEncode(tempRegistrationData)); + } + }, + ); + + return Material( + child: Container( + width: Constant.getActualX(context: context, x: 1160), + // height: MediaQuery.of(context).size.height, + color: Colors.white, + child: Column( + children: [ + Row( + children: [ + SizedBox( + // color: Colors.red, + width: Constant.getActualX(context: context, x: 1160), + // Constant.getActualX(context: context, x: 1160) * 0.71, + child: TestsTableWidget( + specimenLoading: specimenLoading, + testScrollCtr: testScrollCtr, + tempRegistrationData: tempRegistrationData, + addOrderLoading: addOrderLoading, + delete: delete, + deletePaket: deletePaket, + pickDateTime: pickDateTime), + ), + // Container( + // width: + // Constant.getActualX(context: context, x: 1160) * 0.05, + // ), + // Container( + // // color: Colors.red, + // width: + // Constant.getActualX(context: context, x: 1160) * 0.24, + // child: Column( + // children: [ + // Container( + // width: + // Constant.getActualX(context: context, x: 1160) * + // 0.24, + // child: TabBar( + // controller: tabctr, + // labelColor: Colors.black, + // labelStyle: Constant.body3_500(context: context) + // .copyWith(color: Colors.black), + // indicatorColor: Constant.primaryBlue, + // onTap: (value) => selectedTab.value = value, + // tabs: [ + // Tab( + // text: "Specimen", + // ), + // Tab( + // text: "Bahan", + // ) + // ]), + // ), + // SizedBox( + // height: + // Constant.getActualY(context: context, y: 20), + // ), + // if (selectedTab.value == 0) + // SpecimenTableWidget( + // specimenLoading: specimenLoading, + // tempRegistrationData: tempRegistrationData, + // addOrderLoading: addOrderLoading), + // if (selectedTab.value == 1) + // BahanTableWidget( + // specimenLoading: specimenLoading, + // tempRegistrationData: tempRegistrationData, + // addOrderLoading: addOrderLoading) + + // // SizedBox( + // // height: + // // Constant.getActualY(context: context, y: 20), + // // ), + // // BahanTableWidget( + // // specimenLoading: specimenLoading, + // // tempRegistrationData: tempRegistrationData, + // // addOrderLoading: addOrderLoading), + // ], + // ), + // ), + ], + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + OutlinedButton( + style: OutlinedButton.styleFrom( + backgroundColor: Colors.white, + side: BorderSide( + color: Colors.grey.shade400, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: addOrderLoading == false + ? () { + // Navigator.pop(context); + ref + .read(registrasiProvider.state) + .update((state) => state - 1); + } + : null, + child: SizedBox( + height: + Constant.getActualY(context: context, y: 56), + child: Center( + child: addOrderLoading + ? LoadingAnimationWidget.staggeredDotsWave( + color: Constant.green, size: 30) + : Text('Sebelumnya', + style: Constant.body3_600( + context: context) + .copyWith(color: Constant.textBlack)), + ), + )), + SizedBox( + width: Constant.getActualX(context: context, x: 24)), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Constant.green, + side: BorderSide(color: Constant.green, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + + // onPressed: () { + // showDialog( + // context: context, + // builder: ((context) => DialogPemeriksaan())); + // }, + onPressed: addOrderLoading == false + ? () { + addOrder(); + } + : null, + child: SizedBox( + height: + Constant.getActualY(context: context, y: 56), + child: Center( + child: addOrderLoading + ? LoadingAnimationWidget.staggeredDotsWave( + color: Colors.white, size: 30) + : Text('Simpan', + style: Constant.body3_600( + context: context) + .copyWith(color: Constant.textWhite)), + ), + )), + ], + ), + ) + ], + ))); + } +} diff --git a/lib/screen/registrasi/form_pemeriksaan.dart b/lib/screen/registrasi/form_pemeriksaan.dart new file mode 100644 index 0000000..6bc5022 --- /dev/null +++ b/lib/screen/registrasi/form_pemeriksaan.dart @@ -0,0 +1,658 @@ +import 'dart:ui'; + +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/intl.dart'; +import 'package:mitra_corporate/model/auth_model.dart'; +import 'package:mitra_corporate/model/registration_model.dart'; +import 'package:mitra_corporate/model/test_model.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/screen/registrasi/registrasi_prvider.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; +import 'package:scroll_to_index/scroll_to_index.dart'; + +import '../../app/constant.dart'; +import '../../provider/registrasi_provider.dart'; +import '../../widgets/custom_snackbar_widget.dart'; +import '../../widgets/custom_text_field.dart'; + +// buat model tipu2 sebagai penampung list dari data-data +// DummyListPeriksa = idListPeriksa, idTipePeriksa, namaPeriksa +// DummyTipePeriksa = idTipePeriksa, tipePeriksa +class DummyListPeriksa { + late int idListPeriksa; + late int idTipePeriksa; + late String namaPeriksa; + + DummyListPeriksa( + {required this.idListPeriksa, + required this.idTipePeriksa, + required this.namaPeriksa}); +} + +class DummyTipePeriksa { + late int idTipePeriksa; + late String tipePeriksa; + + DummyTipePeriksa({required this.idTipePeriksa, required this.tipePeriksa}); +} + +// ubah stateless jadi HookConsumerWidget +class FormPemeriksaan extends HookConsumerWidget { + const FormPemeriksaan({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final tempRegistrationData = ref.watch(registrationDataProvider); + final selectedtest = ref.watch(selectedTestProvider); + final selectedPacket = ref.watch(selectedPacketProvider); + // inisialisasi baca provider idTipePeriksaStateProvider + final selectedTab = ref.watch(idTipePeriksaStateProvider); + final AuthModel auth = ref.watch(authProvider) ?? AuthModel(); + final listTest = useState>(List.empty()); + final testLoading = useState(false); + final searchCtr = useTextEditingController(text: ""); + final scrollCtr = useScrollController(); + final ctr = useState(AutoScrollController()); + + //get pemeriksaan from api + getPemeriksaan() { + ref + .read(GetFPPProvider.notifier) + .getData(mouID: auth.mUserMMouID ?? "0", token: auth.token ?? "0"); + } + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + getPemeriksaan(); + }); + return () {}; + }, []); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + int index = listTest.value + .indexWhere((element) => element.tabId == selectedTab); + print(index); + // scrollCtr.animateTo(double.parse(index.toString()), + // curve: Curves.linear, duration: Duration(milliseconds: 100)); + // scrollCtr.jumpTo(double.parse(index.toString())); + if (searchCtr.text.isNotEmpty) { + ctr.value.scrollToIndex(index); + } + }); + return () {}; + }, [selectedTab]); + // ref.watch(idTipePeriksaStateProvider.notifier).addListener((state) { + // int index = + // listTest.value.indexWhere((element) => element.tabId == selectedTab); + // print(index); + // if (index != -1) { + // // scrollCtr.animateTo(double.parse(index.toString()), + // // curve: Curves.linear, duration: Duration(milliseconds: 100)); + // // scrollCtr.jumpTo(double.parse(index.toString())); + // } + // }); + + ref.listen( + GetFPPProvider, + (pref, next) { + if (next is GetFPPStateInit) { + testLoading.value = true; + } else if (next is GetFPPStateLoading) { + testLoading.value = true; + } else if (next is GetFPPStateError) { + testLoading.value = false; + print(next.message); + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is GetFPPStateDone) { + listTest.value = next.model; + ref.read(tempTestListProvider.notifier).state = next.model; + + // tabController.length + + testLoading.value = false; + } + }, + ); + + // searchCtr.addListener(() { + // listTest.value.firstWhere((element) => element.items) + // },); + + return Material( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 500), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + suffixIcon: const Icon(EvaIcons.search), + controller: searchCtr, + isPassword: false, + hintText: "Cari Pemeriksaan", + labelText: "Cari Pemeriksaan", + ), + ), + SizedBox(height: Constant.getActualY(context: context, y: 12)), + // Tab + testLoading.value + ? LoadingAnimationWidget.discreteCircle( + color: Colors.blue, size: 20) + : ConstrainedBox( + constraints: BoxConstraints( + maxWidth: Constant.getActualX(context: context, x: 1080)), + child: SizedBox( + width: MediaQuery.of(context).size.width, + height: Constant.getActualY(context: context, y: 70), + child: ScrollConfiguration( + behavior: + ScrollConfiguration.of(context).copyWith(dragDevices: { + PointerDeviceKind.mouse, + PointerDeviceKind.touch, + PointerDeviceKind.trackpad, + PointerDeviceKind.stylus, + PointerDeviceKind.unknown + }), + child: ListView( + controller: ctr.value, + scrollDirection: Axis.horizontal, + children: listTest.value + .asMap() + .entries + .map((e) => AutoScrollTag( + key: ValueKey(e.key), + index: e.key, + controller: ctr.value, + child: Container( + decoration: BoxDecoration( + border: Border( + bottom: selectedTab == e.value.tabId + ? BorderSide( + width: 2, + color: Constant.primaryRed) + : const BorderSide( + color: Colors.transparent), + ), + ), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shadowColor: Constant.primaryRed + .withOpacity(0.5), + backgroundColor: Colors.transparent, + elevation: 0), + onPressed: () { + ref + .read(idTipePeriksaStateProvider + .state) + .update((state) => e.value.tabId!); + }, + child: Text( + e.value.tab!, + style: + Constant.body3_500(context: context) + .copyWith(color: Colors.black), + )), + ), + )) + .toList(), + ), + ), + ), + ), + + SizedBox(height: Constant.getActualY(context: context, y: 14)), + + // widget checkbox semua biar rapi + listCheckboxWidget( + search: searchCtr, + listTest: listTest, + loading: testLoading, + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + SizedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + OutlinedButton( + style: OutlinedButton.styleFrom( + backgroundColor: Colors.white, + side: BorderSide(color: Colors.grey.shade400, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: () { + // Navigator.pop(context); + ref + .read(registrasiProvider.state) + .update((state) => state - 1); + }, + child: SizedBox( + height: Constant.getActualY(context: context, y: 56), + child: Center( + child: Text('Sebelumnya', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack)), + ), + )), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Constant.green, + side: BorderSide(color: Constant.green, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + onPressed: () { + if (selectedtest.isEmpty && selectedPacket.isEmpty) { + SanckbarWidget( + context, + "Belum memilih Pemeriksaan atau paket", + snackbarType.warning); + return; + } + List ts = List.empty(growable: true); + double total = 0; + for (var i = 0; i < selectedtest.length; i++) { + var splitted = selectedtest[i].split("|"); + var tabid = splitted[0]; + var testid = splitted[1]; + for (var j = 0; j < listTest.value.length; j++) { + if (listTest.value[j].tabId == int.parse(tabid)) { + var tests = listTest.value[j].items; + for (var k = 0; k < tests!.length; k++) { + if (tests[k].testID == testid) { + ts.add(Tests( + date: DateFormat('dd-MM-yyyy HH:mm') + .format(DateTime.now()) + .toString(), + id: tests[k].testID, + name: tests[k].testName, + price: tests[k].testPrice, + sasCode: tests[k].sasCode, + // specimen: "", + tab: tabid, + )); + total = total + + double.parse(tests[k].testPrice ?? "0"); + } + } + } + } + } + List pkt = List.empty(growable: true); + for (var i = 0; i < selectedPacket.length; i++) { + var paket = listTest.value + .firstWhere((element) => element.isPaket == "Y"); + paket.items?.forEach((element) { + if (element.testID == selectedPacket[i]) { + pkt.add(Paket( + id: element.testID, + arrTest: element.arrTest, + detail: element.sasCode, + price: element.testPrice, + type: element.type, + name: element.testName)); + total = + total + double.parse(element.testPrice ?? "0"); + } + }); + } + bool paketValidation = false; + bool testValidation = false; + for (var e in pkt) { + var test = e.arrTest?.split(","); + test?.forEach((f) { + for (var g in pkt) { + if (e.id?.trim() != g.id?.trim()) { + var testCek = g.arrTest!.split(","); + var cek = testCek.firstWhere( + (h) => h.trim() == f.trim(), + orElse: () => "-1", + ); + if (cek != "-1") { + paketValidation = true; + } + } + } + }); + } + for (var e in pkt) { + var test = e.arrTest?.split(","); + test?.forEach((f) { + for (var g in ts) { + if (f.trim() == g.id?.trim()) { + testValidation = true; + } + } + }); + } + if (paketValidation) { + SanckbarWidget( + context, + "Paket Pilihan memiliki pemeriksaan yang sama ", + snackbarType.warning); + return; + } + if (testValidation) { + SanckbarWidget( + context, + "Paket Pilihan memiliki pemeriksaan yang sama dengan test pilihan", + snackbarType.warning); + return; + } + + ref.read(registrationDataProvider.notifier).state = + RegistrationModel( + orderID: tempRegistrationData.orderID, + patientId: tempRegistrationData.patientId, + token: auth.token, + patientData: tempRegistrationData.patientData, + specimens: tempRegistrationData.specimens, + bahan: tempRegistrationData.bahan, + tests: ts, + paket: pkt, + total: total.toString()); + ref.read(registrasiProvider.state).update((state) => 2); + }, + child: SizedBox( + height: Constant.getActualY(context: context, y: 56), + child: Center( + child: Text('Berikutnya', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textWhite)), + ), + )), + ], + ), + ) + ], + )); + } +} + +class listCheckboxWidget extends HookConsumerWidget { + const listCheckboxWidget( + {super.key, + required this.listTest, + required this.loading, + required this.search}); + final ValueNotifier> listTest; + final ValueNotifier loading; + final TextEditingController search; + + @override + Widget build(BuildContext context, WidgetRef ref) { + bool? isChecked = false; + final tests = useState>(List.empty()); + final isPaket = useState(false); + + // inisialisasi state provider buat baca idTipePeriksa yang di klik + final selectedTab = ref.watch(idTipePeriksaStateProvider); + final selectedTest = ref.watch(selectedTestProvider); + final selectedPacket = ref.watch(selectedPacketProvider); + final scrollCtr = useScrollController(); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + if (loading.value == false && listTest.value.isNotEmpty) { + // TestModel temp = listTest.value[0]; + var selected = listTest.value + .firstWhere((element) => element.tabId == selectedTab); + if (selected.isPaket == 'Y') { + isPaket.value = true; + } else { + isPaket.value = false; + } + if (search.text.isNotEmpty) { + List filter = selected.items! + .where((element) => + element.testName!.toLowerCase().contains(search.text)) + .toList(); + tests.value = filter; + } else { + tests.value = selected.items ?? []; + } + } + }); + return () {}; + }, [selectedTab, loading.value]); + + search.addListener( + () { + if (loading.value == false && listTest.value.isNotEmpty) { + for (var i = 0; i < listTest.value.length; i++) { + List flt = listTest.value[i].items! + .where((element) => element.testName! + .toLowerCase() + .contains(search.text.toLowerCase())) + .toList(); + if (flt.isNotEmpty) { + ref.read(idTipePeriksaStateProvider.notifier).state = + listTest.value[i].tabId!; + break; + } + } + + var selected = listTest.value + .firstWhere((element) => element.tabId == selectedTab); + + if (search.text.isNotEmpty) { + List filter = selected.items! + .where((element) => element.testName! + .toLowerCase() + .contains(search.text.toLowerCase())) + .toList(); + tests.value = filter; + } else { + tests.value = selected.items ?? []; + } + } + }, + ); + + selectedItem(int idTab, String idTest) { + List temp = selectedTest.toSet().toList(growable: true); + var data = "$idTab|$idTest"; + var cek = temp.firstWhere( + (element) => element == data, + orElse: () => "-1", + ); + if (cek == "-1") { + temp.add(data); + } else { + temp.remove(cek); + } + ref.read(selectedTestProvider.notifier).state = temp.toSet().toList(); + } + + valueCek(int idTab, String idTest) { + var data = "$idTab|$idTest"; + var cek = selectedTest.firstWhere( + (element) => element == data, + orElse: () => "-1", + ); + if (cek == "-1") { + return false; + } else { + return true; + } + } + + selectPacket(String idTest) { + List temp = selectedPacket.toSet().toList(growable: true); + + var cek = temp.firstWhere( + (element) => element == idTest, + orElse: () => "-1", + ); + if (cek == "-1") { + temp.add(idTest); + } else { + temp.remove(cek); + } + ref.read(selectedPacketProvider.notifier).state = temp.toSet().toList(); + } + + valueCekPacket(String idTest) { + var cek = selectedPacket.firstWhere( + (element) => element == idTest, + orElse: () => "-1", + ); + if (cek == "-1") { + return false; + } else { + return true; + } + } + + // buat data2 dari List Periksa + + return Row(children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 1080), + height: Constant.getActualY(context: context, y: 230), + child: loading.value + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 30), + ) + : Scrollbar( + controller: scrollCtr, + thumbVisibility: true, + trackVisibility: true, + child: ScrollConfiguration( + behavior: + ScrollConfiguration.of(context).copyWith(dragDevices: { + PointerDeviceKind.mouse, + PointerDeviceKind.touch, + PointerDeviceKind.trackpad, + PointerDeviceKind.stylus, + PointerDeviceKind.unknown + }), + child: isPaket.value + ? ListView( + controller: scrollCtr, + children: tests.value + .map( + (e) => Container( + margin: EdgeInsets.symmetric(vertical: 5), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + children: [ + Checkbox( + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(4)), + // value: valueCek( + // selectedTab, e.testID!), + value: valueCekPacket(e.testID!), + onChanged: (value) { + print(value); + selectPacket(e.testID!); + // selectedItem( + // selectedTab, e.testID!); + }), + SizedBox( + // constraints: BoxConstraints( + // maxWidth: + // ), + // color: Colors.red, + width: Constant.getActualX( + context: context, x: 380), + child: Text('${e.testName}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Constant.body3_400( + context: context) + .copyWith( + color: Constant + .textBlack)), + ), + ], + ), + Padding( + padding: EdgeInsets.only(left: 30), + child: Text( + e.sasCode ?? "", + style: Constant.caption2_400( + context: context) + .copyWith( + color: Constant.textGrey), + ), + ) + ], + ), + ), + ) + .toList(), + ) + : GridView.count( + controller: scrollCtr, + shrinkWrap: true, + crossAxisCount: 2, + childAspectRatio: 15, + children: tests.value + .map( + (e) => Container( + child: Row( + children: [ + Checkbox( + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(4)), + // value: valueCek(selectedTab, e.testID!), + value: + valueCek(selectedTab, e.testID!), + onChanged: (value) { + print(value); + selectedItem( + selectedTab, e.testID!); + }), + SizedBox( + // constraints: BoxConstraints( + // maxWidth: + // ), + // color: Colors.red, + width: Constant.getActualX( + context: context, x: 380), + child: Text('${e.testName}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Constant.body3_400( + context: context) + .copyWith( + color: Constant.textBlack)), + ), + // Container( + // // constraints: BoxConstraints( + // // maxWidth: + // // ), + // // color: Colors.red, + // // width: + // // Constant.getActualX(context: context, x: 350), + // child: Text( + // Constant.convertToIdr( + // int.parse(e.testPrice ?? "0"), 0), + // style: Constant.body3_500( + // context: context) + // .copyWith(color: Constant.textBlack)), + // ), + ], + ), + ), + ) + .toList(), + ), + ), + ), + ), + ]); + } +} diff --git a/lib/screen/registrasi/get_filter_provider.dart b/lib/screen/registrasi/get_filter_provider.dart new file mode 100644 index 0000000..d3dd343 --- /dev/null +++ b/lib/screen/registrasi/get_filter_provider.dart @@ -0,0 +1,66 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/registration_filter_model.dart'; +import 'package:mitra_corporate/repository/registration_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class GetFilterRegistrationState extends Equatable { + final DateTime date; + const GetFilterRegistrationState(this.date); + @override + List get props => [date]; +} + +class GetFilterRegistrationStateInit extends GetFilterRegistrationState { + GetFilterRegistrationStateInit() : super(DateTime.now()); +} + +class GetFilterRegistrationStateLoading extends GetFilterRegistrationState { + GetFilterRegistrationStateLoading() : super(DateTime.now()); +} + +class GetFilterRegistrationStateError extends GetFilterRegistrationState { + final String message; + GetFilterRegistrationStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class GetFilterRegistrationStateDone extends GetFilterRegistrationState { + final RegistrationFilterModel model; + GetFilterRegistrationStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class GetFilterRegistrationNotifier + extends StateNotifier { + final Ref ref; + GetFilterRegistrationNotifier({ + required this.ref, + }) : super(GetFilterRegistrationStateInit()); + + void getData({required String token}) async { + try { + state = GetFilterRegistrationStateLoading(); + final dio = ref.read(dioProvider); + final resp = + await RegistrationRepository(dio: dio).getFilter(token: token); + state = GetFilterRegistrationStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = GetFilterRegistrationStateError(message: e.message.toString()); + } else { + state = GetFilterRegistrationStateError(message: e.toString()); + } + } + } +} + +//provider +final GetFilterRegistrationProvider = StateNotifierProvider< + GetFilterRegistrationNotifier, GetFilterRegistrationState>( + (ref) => GetFilterRegistrationNotifier(ref: ref)); diff --git a/lib/screen/registrasi/get_specimen_provider.dart b/lib/screen/registrasi/get_specimen_provider.dart new file mode 100644 index 0000000..08b2327 --- /dev/null +++ b/lib/screen/registrasi/get_specimen_provider.dart @@ -0,0 +1,67 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/registration_model.dart'; +import 'package:mitra_corporate/repository/registration_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class GetSpecimenState extends Equatable { + final DateTime date; + const GetSpecimenState(this.date); + @override + List get props => [date]; +} + +class GetSpecimenStateInit extends GetSpecimenState { + GetSpecimenStateInit() : super(DateTime.now()); +} + +class GetSpecimenStateLoading extends GetSpecimenState { + GetSpecimenStateLoading() : super(DateTime.now()); +} + +class GetSpecimenStateError extends GetSpecimenState { + final String message; + GetSpecimenStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class GetSpecimenStateDone extends GetSpecimenState { + final List model; + GetSpecimenStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class GetSpecimenNotifier extends StateNotifier { + final Ref ref; + GetSpecimenNotifier({ + required this.ref, + }) : super(GetSpecimenStateInit()); + + void getData( + {required List> arrTest, + required String token}) async { + try { + state = GetSpecimenStateLoading(); + final dio = ref.read(dioProvider); + final resp = await RegistrationRepository(dio: dio) + .getSpecimen(arrTest: arrTest, token: token); + state = GetSpecimenStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = GetSpecimenStateError(message: e.message.toString()); + } else { + state = GetSpecimenStateError(message: e.toString()); + } + } + } +} + +//provider +final GetSpecimenProvider = + StateNotifierProvider( + (ref) => GetSpecimenNotifier(ref: ref)); diff --git a/lib/screen/registrasi/new_order_provider.dart b/lib/screen/registrasi/new_order_provider.dart new file mode 100644 index 0000000..64aeae3 --- /dev/null +++ b/lib/screen/registrasi/new_order_provider.dart @@ -0,0 +1,64 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/registration_model.dart'; +import 'package:mitra_corporate/repository/registration_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class NewOrderProviderState extends Equatable { + final DateTime date; + const NewOrderProviderState(this.date); + @override + List get props => [date]; +} + +class NewOrderProviderStateInit extends NewOrderProviderState { + NewOrderProviderStateInit() : super(DateTime.now()); +} + +class NewOrderProviderStateLoading extends NewOrderProviderState { + NewOrderProviderStateLoading() : super(DateTime.now()); +} + +class NewOrderProviderStateError extends NewOrderProviderState { + final String message; + NewOrderProviderStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class NewOrderProviderStateDone extends NewOrderProviderState { + final String number; + NewOrderProviderStateDone({ + required this.number, + }) : super(DateTime.now()); +} + +//notifier +class NewOrderProviderNotifier extends StateNotifier { + final Ref ref; + NewOrderProviderNotifier({ + required this.ref, + }) : super(NewOrderProviderStateInit()); + + void addOrder({required RegistrationModel prm}) async { + try { + state = NewOrderProviderStateLoading(); + final dio = ref.read(dioProvider); + final resp = await RegistrationRepository(dio: dio).addOrder(prm: prm); + state = NewOrderProviderStateDone(number: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = NewOrderProviderStateError(message: e.message.toString()); + } else { + state = NewOrderProviderStateError(message: e.toString()); + } + } + } +} + +//provider +final NewOrderProviderProvider = + StateNotifierProvider( + (ref) => NewOrderProviderNotifier(ref: ref)); diff --git a/lib/screen/registrasi/registrasi_pasien_screen.dart b/lib/screen/registrasi/registrasi_pasien_screen.dart new file mode 100644 index 0000000..8586f0e --- /dev/null +++ b/lib/screen/registrasi/registrasi_pasien_screen.dart @@ -0,0 +1,116 @@ +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/provider/menu_provider.dart'; +import 'package:mitra_corporate/screen/registrasi/dialog_pendaftaran_pasien.dart'; + +import 'package:mitra_corporate/screen/registrasi/table_pasien.dart'; +import 'package:mitra_corporate/widgets/header.dart'; + +import '../../app/constant.dart'; +import '../../provider/registrasi_provider.dart'; +import '../../widgets/custom_text_field.dart'; + +class RegistrasiPasienScreen extends HookConsumerWidget { + const RegistrasiPasienScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final isExpand = ref.watch(sideBarExpandProvider); + + final keywordCtr = + useTextEditingController(text: ref.read(keywordProvider)); + return Scaffold( + // backgroundColor: Constant.grey_200, + // backgroundColor: Color(0xffF9FAFB), + body: Column( + children: [ + Header(), + Padding( + // padding: const EdgeInsets.all(0), + padding: const EdgeInsets.all(32.0), + child: Column( + children: [ + Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 1079), + // height: Constant.getActualY(context: context, y: 40), + child: Text('Registrasi Pasien', + style: Constant.h3_400(context: context) + .copyWith(color: Constant.textBlack)), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 40), + ), + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12)), + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 32), + ), + child: Column( + children: [ + Row( + children: [ + Expanded( + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + controller: keywordCtr, + onChange: (value) { + ref.read(keywordProvider.notifier).state = + value; + }, + isPassword: false, + hintText: "Cari Nama, NIK, dan Nomor HP", + labelText: "Cari Nama, NIK, dan Nomor HP", + suffixIcon: Icon(EvaIcons.search), + ), + ), + SizedBox( + width: + Constant.getActualX(context: context, x: 24)), + SizedBox( + width: + Constant.getActualX(context: context, x: 171), + height: + Constant.getActualY(context: context, y: 56), + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(8), + // color: Constant.primaryRed), + child: ElevatedButton.icon( + label: Text('Pasien Baru', + style: Constant.button_large(context: context) + .copyWith(color: Constant.textWhite)), + icon: Icon(EvaIcons.plus, + color: Colors.white, size: 20.0), + onPressed: () { + ref + .read(dialogOrderActionProvider.notifier) + .state = 'new'; + showDialog( + context: context, + barrierDismissible: false, + builder: ((context) => + DialogPendaftaranPasien())); + }, + ), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 24)), + TablePasien() + ], + ), + ), + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/screen/registrasi/registrasi_prvider.dart b/lib/screen/registrasi/registrasi_prvider.dart new file mode 100644 index 0000000..7486d90 --- /dev/null +++ b/lib/screen/registrasi/registrasi_prvider.dart @@ -0,0 +1,64 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/test_model.dart'; +import 'package:mitra_corporate/repository/registration_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class GetFPPState extends Equatable { + final DateTime date; + const GetFPPState(this.date); + @override + List get props => [date]; +} + +class GetFPPStateInit extends GetFPPState { + GetFPPStateInit() : super(DateTime.now()); +} + +class GetFPPStateLoading extends GetFPPState { + GetFPPStateLoading() : super(DateTime.now()); +} + +class GetFPPStateError extends GetFPPState { + final String message; + GetFPPStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class GetFPPStateDone extends GetFPPState { + final List model; + GetFPPStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class GetFPPNotifier extends StateNotifier { + final Ref ref; + GetFPPNotifier({ + required this.ref, + }) : super(GetFPPStateInit()); + + void getData({required String mouID, required String token}) async { + try { + state = GetFPPStateLoading(); + final dio = ref.read(dioProvider); + final resp = await RegistrationRepository(dio: dio) + .getFPP(mouID: mouID, token: token); + state = GetFPPStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = GetFPPStateError(message: e.message.toString()); + } else { + state = GetFPPStateError(message: e.toString()); + } + } + } +} + +//provider +final GetFPPProvider = StateNotifierProvider( + (ref) => GetFPPNotifier(ref: ref)); diff --git a/lib/screen/registrasi/registrasi_sukses.dart b/lib/screen/registrasi/registrasi_sukses.dart new file mode 100644 index 0000000..dd1f56b --- /dev/null +++ b/lib/screen/registrasi/registrasi_sukses.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/model/registration_model.dart'; +import 'package:mitra_corporate/provider/registrasi_provider.dart'; + +import '../../app/constant.dart'; + +class RegistrasiSukses extends HookConsumerWidget { + const RegistrasiSukses({ + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final orderNumber = ref.watch(orderNumberProvider); + return Column( + children: [ + SizedBox( + // color: Colors.green, + width: Constant.getActualX(context: context, x: 500), + height: Constant.getActualY(context: context, y: 500), + child: Image.asset( + 'images/order_success.png', + fit: BoxFit.contain, + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + Text('Registrastion Success', + style: Constant.h3_400(context: context) + .copyWith(color: Constant.textBlack)), + SizedBox( + height: Constant.getActualY(context: context, y: 48), + ), + SelectableText('Order Number : $orderNumber', + style: Constant.h3_400(context: context) + .copyWith(color: Constant.textBlack)), + SizedBox( + height: Constant.getActualY(context: context, y: 48), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 48), + child: OutlinedButton( + style: OutlinedButton.styleFrom( + backgroundColor: Colors.white, + side: BorderSide(color: Constant.primaryRed, width: 1), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: () { + Navigator.pop(context); + ref.read(registrasiProvider.notifier).state = 0; + ref.read(idTipePeriksaStateProvider.notifier).state = 1; + ref.read(registrationDataProvider.notifier).state = + RegistrationModel(); + ref.read(selectedTestProvider.notifier).state = + List.empty(growable: true); + ref.read(tempTestListProvider.notifier).state = + List.empty(growable: true); + ref.read(selectedPacketProvider.notifier).state = + List.empty(growable: true); + }, + child: Text('Kembali ke Dashboard', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.primaryRed))), + ), + ], + ); + } +} diff --git a/lib/screen/registrasi/search_patient_provider.dart b/lib/screen/registrasi/search_patient_provider.dart new file mode 100644 index 0000000..d1c2d33 --- /dev/null +++ b/lib/screen/registrasi/search_patient_provider.dart @@ -0,0 +1,74 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/search_patient_model.dart'; +import 'package:mitra_corporate/repository/patient_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class SearchPatientState extends Equatable { + final DateTime date; + const SearchPatientState(this.date); + @override + List get props => [date]; +} + +class SearchPatientStateInit extends SearchPatientState { + SearchPatientStateInit() : super(DateTime.now()); +} + +class SearchPatientStateLoading extends SearchPatientState { + SearchPatientStateLoading() : super(DateTime.now()); +} + +class SearchPatientStateError extends SearchPatientState { + final String message; + SearchPatientStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class SearchPatientStateDone extends SearchPatientState { + final SearchPatientModel model; + SearchPatientStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class SearchPatientNotifier extends StateNotifier { + final Ref ref; + SearchPatientNotifier({ + required this.ref, + }) : super(SearchPatientStateInit()); + + void getData( + {required String token, + required String page, + required String keyword, + required String rpp, + required String companyID}) async { + try { + state = SearchPatientStateLoading(); + final dio = ref.read(dioProvider); + final resp = await PatientRepository(dio: dio).search( + companyID: companyID, + keyword: keyword, + page: page, + rpp: rpp, + token: token); + state = SearchPatientStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = SearchPatientStateError(message: e.message.toString()); + } else { + state = SearchPatientStateError(message: e.toString()); + } + } + } +} + +//provider +final SearchPatientProvider = + StateNotifierProvider( + (ref) => SearchPatientNotifier(ref: ref)); diff --git a/lib/screen/registrasi/table_pasien.dart b/lib/screen/registrasi/table_pasien.dart new file mode 100644 index 0000000..971e505 --- /dev/null +++ b/lib/screen/registrasi/table_pasien.dart @@ -0,0 +1,488 @@ +import 'dart:async'; + +import 'package:mitra_corporate/screen/registrasi/delete_patient_provider%20copy.dart'; +import 'package:mitra_corporate/screen/registrasi/dialog_delete_patient.dart'; +import 'package:mitra_corporate/screen/registrasi/dialog_edit_patient.dart'; +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:data_table_2/data_table_2.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/intl.dart'; +import 'package:mitra_corporate/model/patient_Model.dart'; +import 'package:mitra_corporate/model/registration_model.dart'; +import 'package:mitra_corporate/model/search_patient_model.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/provider/registrasi_provider.dart'; +import 'package:mitra_corporate/screen/registrasi/search_patient_provider.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../provider/menu_provider.dart'; +import '../../widgets/custom_snackbar_widget.dart'; +import 'dialog_pendaftaran_pasien.dart'; + +class TablePasien extends HookConsumerWidget { + const TablePasien({ + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final isExpand = ref.watch(sideBarExpandProvider); + final auth = ref.watch(authProvider); + final rowsPerPage = useState(10); + final searchLoading = useState(false); + final deleteLoading = useState(false); + final dataStart = useState(1); + final dataEnd = useState(1); + final currPage = useState(1); + final patientList = useState( + SearchPatientModel(patients: [], total: "0", totalPage: 0)); + final keyword = ref.watch(keywordProvider); + final registrasiTabID = ref.watch(registrasiProvider); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final debouncer = Debouncer(milliseconds: 100); + debouncer.run(() { + ref.read(SearchPatientProvider.notifier).getData( + token: auth?.token ?? "", + page: currPage.value.toString(), + keyword: keyword, + rpp: rowsPerPage.value.toString(), + companyID: auth?.mUserMCompanyID ?? ""); + }); + }); + return () {}; + }, [currPage.value, registrasiTabID]); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final debouncer = Debouncer(milliseconds: 100); + currPage.value = 1; + debouncer.run(() { + ref.read(SearchPatientProvider.notifier).getData( + token: auth?.token ?? "", + page: "1", + keyword: keyword, + rpp: rowsPerPage.value.toString(), + companyID: auth?.mUserMCompanyID ?? ""); + }); + }); + return () {}; + }, [ + keyword, + rowsPerPage.value, + ]); + + ref.listen( + SearchPatientProvider, + (pref, next) { + if (next is SearchPatientStateInit) { + searchLoading.value = true; + } else if (next is SearchPatientStateLoading) { + searchLoading.value = true; + } else if (next is SearchPatientStateError) { + searchLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is SearchPatientStateDone) { + patientList.value = next.model; + // print(jsonEncode(next.model)); + + dataStart.value = + (currPage.value * rowsPerPage.value) - rowsPerPage.value + 1; + dataEnd.value = currPage.value == next.model.totalPage + ? int.parse(next.model.total ?? "0") + : dataStart.value + rowsPerPage.value - 1; + + searchLoading.value = false; + } + }, + ); + openDialog(PatientModel e) { + ref.read(registrationDataProvider.notifier).state = RegistrationModel( + patientId: e.id, + patientData: PatientData( + noRM: e.noRM, + address: e.address, + dob: e.dob, + gender: e.sexId, + hp: e.hp, + name: e.name, + nik: e.nik, + nip: e.nip, + prefix: e.prefix, + kedudukan: e.kedudukan, + jabatan: e.jabatan, + lokasi: e.lokasi, + pekerjaan: e.pekerjaan, + saluation: e.titleId, + suffix: e.suffix), + ); + ref.read(dialogOrderActionProvider.notifier).state = 'new'; + showDialog( + context: context, + barrierDismissible: false, + builder: ((context) => DialogPendaftaranPasien())); + } + + openDialogEdit(PatientModel e) { + ref.read(registrationEditPatientProvider.notifier).state = + RegistrationModel( + patientId: e.id, + patientData: PatientData( + noRM: e.noRM, + address: e.address, + dob: e.dob, + gender: e.sexId, + hp: e.hp, + name: e.name, + nik: e.nik, + nip: e.nip, + prefix: e.prefix, + kedudukan: e.kedudukan, + jabatan: e.jabatan, + lokasi: e.lokasi, + pekerjaan: e.pekerjaan, + saluation: e.titleId, + suffix: e.suffix), + ); + // showDialog( + // context: context, + // barrierDismissible: false, + // builder: (BuildContext context) => DialogSendQr( + // arrOrderID: ["1", "2"], + // orderNumber: "abc", + // ), + // ); + showDialog( + context: context, + barrierDismissible: false, + builder: ((context) => SimpleDialog( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(12))), + contentPadding: const EdgeInsets.all(40.0), + children: [DialogEditPatient()], + ))).then((value) { + print(value); + if (value == "done") { + ref.read(SearchPatientProvider.notifier).getData( + token: auth?.token ?? "", + page: currPage.value.toString(), + keyword: keyword, + rpp: rowsPerPage.value.toString(), + companyID: auth?.mUserMCompanyID ?? ""); + } + }); + } + + openDialogDelete(PatientModel e) { + showDialog( + context: context, + builder: (context) { + return DialogDeletePatient( + NIK: e.nik ?? "", + delete: (patientID) { + print(patientID); + ref.read(DeletePatientProvider.notifier).DeletePatent( + patient_id: patientID, token: auth?.token ?? ''); + }, + patientID: e.id ?? "", + loading: deleteLoading.value, + name: e.name ?? ""); + }, + ); + } + + ref.listen( + DeletePatientProvider, + (pref, next) { + if (next is DeletePatientProviderStateInit) { + deleteLoading.value = true; + } else if (next is DeletePatientProviderStateLoading) { + deleteLoading.value = true; + } else if (next is DeletePatientProviderStateError) { + deleteLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is DeletePatientProviderStateDone) { + SanckbarWidget( + context, "Berhasil mengapus data", snackbarType.success); + Navigator.pop(context); + ref.read(SearchPatientProvider.notifier).getData( + token: auth?.token ?? "", + page: currPage.value.toString(), + keyword: keyword, + rpp: rowsPerPage.value.toString(), + companyID: auth?.mUserMCompanyID ?? ""); + deleteLoading.value = false; + } + }, + ); + return Material( + // elevation: 8, + borderRadius: BorderRadius.circular(8), + child: Container( + + // width: Constant.getActualX(context: context, x: 1079), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + // color: Colors.red, + ), + height: Constant.getActualY(context: context, y: 586), + child: Column( + children: [ + Container( + color: Colors.white, + height: Constant.getActualY(context: context, y: 516), + // height: Constant.getActualY(context: context, y: 300), + child: DataTable2( + dividerThickness: 1, + headingRowColor: WidgetStatePropertyAll(Constant.grey_200), + columnSpacing: 12, + horizontalMargin: 15, + minWidth: 600, + empty: Text("Data tidak ditemukan"), + columns: [ + DataColumn2( + label: Text('Nama Pasien', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + size: ColumnSize.M, + ), + DataColumn2( + label: Text('Alamat', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + size: ColumnSize.L), + DataColumn( + label: Text('NIK', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + DataColumn( + label: Text('DOB', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + DataColumn( + label: Text('Nomor HP', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + DataColumn2( + fixedWidth: + Constant.getActualX(context: context, x: 120), + label: Text('Aksi', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + // size: ColumnSize.S, + ), + ], + rows: searchLoading.value + ? [ + DataRow(cells: [ + DataCell(LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20)), + DataCell(LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20)), + DataCell(LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20)), + DataCell(LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20)), + DataCell(LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20)), + DataCell(LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20)), + ]) + ] + : patientList.value.patients! + .map((e) => DataRow( + onLongPress: () { + // openDialog(e); + }, + cells: [ + DataCell(SelectableText(e.name ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: Constant.textBlack))), + DataCell(SelectableText(e.address ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: Constant.textBlack))), + DataCell(SelectableText(e.nik ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: Constant.textBlack))), + DataCell(SelectableText( + DateFormat('dd-MM-yyyy') + .format(DateTime.parse(e.dob!)), + style: Constant.body3_600( + context: context) + .copyWith( + color: Constant.textBlack))), + DataCell(SelectableText(e.hp ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: Constant.textBlack))), + DataCell(Row( + children: [ + Tooltip( + message: "Tambah order ${e.name}", + child: IconButton( + onPressed: () { + openDialog(e); + }, + icon: Icon( + EvaIcons.plusSquare, + color: Colors.grey, + ), + ), + ), + Tooltip( + message: "Edit Patient ${e.name}", + child: IconButton( + onPressed: () { + openDialogEdit(e); + }, + icon: Icon( + EvaIcons.edit2, + color: Colors.grey, + ), + ), + ), + if (e.statusDelete == "Y") + Tooltip( + message: + "Delete Patient ${e.name}", + child: IconButton( + onPressed: () { + openDialogDelete(e); + }, + icon: Icon( + EvaIcons.closeCircle, + color: Colors.grey, + ), + ), + ), + ], + )), + ])) + .toList()), + ), + Material( + borderRadius: BorderRadius.circular(8), + color: Colors.white, + child: SizedBox( + // decoration: BoxDecoration( + // color: Colors.white, + // shape: BoxShape.rectangle, + // borderRadius: BorderRadius.circular(8)), + height: Constant.getActualY(context: context, y: 70), + child: Row( + children: [ + Spacer(), + Text( + "Rows per page :", + style: Constant.body3_400(context: context), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + DropdownButton( + value: rowsPerPage.value, + dropdownColor: Colors.white, + elevation: 0, + style: Constant.body3_400(context: context) + .copyWith(color: Colors.black), + underline: Container(), + icon: Icon(Icons.keyboard_arrow_down_rounded), + focusColor: Colors.white, + items: [ + DropdownMenuItem( + value: 10, + child: Text("10"), + ), + DropdownMenuItem( + value: 15, + child: Text("15"), + ), + DropdownMenuItem( + value: 20, + child: Text("20"), + ), + DropdownMenuItem( + value: 30, + child: Text("30"), + ), + DropdownMenuItem( + value: 40, + child: Text("40"), + ), + DropdownMenuItem( + value: 50, + child: Text("50"), + ), + ], + onChanged: (value) { + rowsPerPage.value = value!; + }), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + Text( + "${dataStart.value} - ${dataEnd.value} of ${patientList.value.total}", + style: Constant.body3_400(context: context), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + IconButton( + iconSize: 20, + onPressed: currPage.value > 1 + ? () { + currPage.value = currPage.value - 1; + } + : null, + icon: Icon(Icons.arrow_back_ios_new_rounded)), + SizedBox( + width: Constant.getActualX(context: context, x: 10), + ), + IconButton( + iconSize: 20, + onPressed: currPage.value < + patientList.value.totalPage!.toInt() + ? () { + currPage.value = currPage.value + 1; + } + : null, + icon: Icon(Icons.arrow_forward_ios_outlined)), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ) + ], + ), + ), + ) + ], + )), + ); + } +} + +class Debouncer { + Debouncer({required this.milliseconds}); + final int milliseconds; + Timer? _timer; + void run(VoidCallback action) { + if (_timer?.isActive ?? false) { + _timer?.cancel(); + } + _timer = Timer(Duration(milliseconds: milliseconds), action); + } +} diff --git a/lib/screen/result/detail_result_screen.dart b/lib/screen/result/detail_result_screen.dart new file mode 100644 index 0000000..ed9c44f --- /dev/null +++ b/lib/screen/result/detail_result_screen.dart @@ -0,0 +1,436 @@ +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../model/surat_jalan_detail_model.dart'; +import '../../provider/auth_provider.dart'; +import '../../provider/menu_provider.dart'; +import '../../provider/surat_jalan_provider.dart'; +import '../../widgets/custom_snackbar_widget.dart'; +import '../../widgets/header.dart'; +import '../../widgets/dialog_print.dart'; +import '../surat_jalan/get_surat_jalan_detail_provider.dart'; + +class DetailResultScreen extends HookConsumerWidget { + const DetailResultScreen({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final sjNumber = ref.watch(suratJalanNumber); + final id = ref.watch(suratJalanID); + final auth = ref.watch(authProvider); + final loading = useState(false); + final data = useState>(List.empty()); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + ref + .read(GetSuratJalanDetailProvider.notifier) + .getData(token: auth?.token ?? "", deliveryID: id); + }); + return () {}; + }, [id]); + ref.listen( + GetSuratJalanDetailProvider, + (pref, next) { + if (next is GetSuratJalanDetailStateInit) { + loading.value = true; + } else if (next is GetSuratJalanDetailStateLoading) { + loading.value = true; + } else if (next is GetSuratJalanDetailStateError) { + loading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is GetSuratJalanDetailStateDone) { + // print(jsonEncode(next.model)); + data.value = next.model; + + loading.value = false; + } + }, + ); + return Material( + child: Column( + children: [ + Header(), + Container( + child: Padding( + padding: const EdgeInsets.all(32.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + IconButton( + onPressed: () { + ref.read(currentMenuProvider.notifier).state = 4; + }, + icon: Icon(EvaIcons.arrowBackOutline)), + Text(sjNumber, + style: Constant.h4_600(context: context) + .copyWith(color: Constant.textBlack)), + ], + ), + + SizedBox( + height: Constant.getActualY(context: context, y: 24)), + // SizedBox( + // height: Constant.getActualY(context: context, y: 24)), + Material( + elevation: 10, + borderRadius: BorderRadius.all(Radius.circular(8)), + child: Column( + children: [ + Container( + height: Constant.getActualY(context: context, y: 64), + decoration: BoxDecoration( + color: Constant.grey_200, + ), + child: Row( + children: [ + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 100), + height: Constant.getActualY( + context: context, y: 64), + color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: Text('Tanggal', + style: + Constant.body3_600(context: context) + .copyWith( + color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 115), + height: Constant.getActualY( + context: context, y: 64), + color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: Text('No Lab', + style: + Constant.body3_600(context: context) + .copyWith( + color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 196), + height: Constant.getActualY( + context: context, y: 64), + color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: Text('Nama Pasien', + style: + Constant.body3_600(context: context) + .copyWith( + color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 100), + height: Constant.getActualY( + context: context, y: 64), + color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: Text('Receive', + overflow: TextOverflow.ellipsis, + style: + Constant.body3_600(context: context) + .copyWith( + color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 100), + height: Constant.getActualY( + context: context, y: 64), + color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: Text('Proses', + overflow: TextOverflow.ellipsis, + style: + Constant.body3_600(context: context) + .copyWith( + color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 80), + height: Constant.getActualY( + context: context, y: 64), + color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: Text('Done', + overflow: TextOverflow.ellipsis, + style: + Constant.body3_600(context: context) + .copyWith( + color: Constant.textGrey)), + ), + ), + ), + ], + ), + ), + Container( + color: Colors.white, + height: Constant.getActualY(context: context, y: 700), + child: loading.value + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 40), + ) + : ListView( + children: data.value + .map( + (e) => Row( + children: [ + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 100), + height: Constant.getActualY( + context: context, y: 64), + + child: Padding( + padding: + const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: Text(e.date ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: Constant + .textBlack)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + width: Constant.getActualX( + context: context, x: 115), + height: Constant.getActualY( + context: context, y: 64), + child: Padding( + padding: + const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: Text( + e.orderNumber ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: Constant + .textBlack)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + width: Constant.getActualX( + context: context, x: 196), + height: Constant.getActualY( + context: context, y: 64), + child: Padding( + padding: + const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: Text( + e.patientName ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: Constant + .textBlack)), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.centerLeft, + width: Constant.getActualX( + context: context, x: 100), + height: Constant.getActualY( + context: context, y: 64), + child: Padding( + padding: + const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: e.status == "R" || + e.status == "T" || + e.status == "P" || + e.status == "Q" || + e.status == "D" + ? Icon( + EvaIcons + .checkmarkCircle2, + size: 24, + color: + Constant.green, + ) + : null), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.centerLeft, + width: Constant.getActualX( + context: context, x: 100), + height: Constant.getActualY( + context: context, y: 64), + child: Padding( + padding: + const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: e.status == "P" || + e.status == "Q" || + e.status == "D" + ? Icon( + EvaIcons + .checkmarkCircle2, + size: 24, + color: + Constant.green, + ) + : null), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.centerLeft, + width: Constant.getActualX( + context: context, x: 80), + height: Constant.getActualY( + context: context, y: 64), + child: Padding( + padding: + const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: e.status == "D" + ? InkWell( + onTap: () { + showDialog( + context: + context, + // barrierDismissible: false, + builder: + ((context) => + Container( + child: + DialogPrint(url: "http://devone.aplikasi.web.id/birt/run?__report=report/one/lab/rpt_fo_001.rptdesign&__format=pdf&username=ADMIN&PID=131868?t=1696927533769"), + ))); + }, + child: Tooltip( + message: + "Cetak laporan", + child: Icon( + EvaIcons + .printer, + size: 24, + color: Constant + .primaryBlue, + ), + ), + ) + : null), + ), + ), + ], + ), + ) + .toList(), + ), + ), + ], + ), + ) + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/screen/result/result_screen.dart b/lib/screen/result/result_screen.dart new file mode 100644 index 0000000..962373b --- /dev/null +++ b/lib/screen/result/result_screen.dart @@ -0,0 +1,532 @@ +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/provider/menu_provider.dart'; +import 'package:mitra_corporate/screen/result/search_result_provider.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../model/surat_jalan_model.dart'; +import '../../provider/auth_provider.dart'; +import '../../provider/surat_jalan_provider.dart'; +import '../../widgets/custom_snackbar_widget.dart'; +import '../../widgets/custom_text_field.dart'; +import '../../widgets/header.dart'; +import '../registrasi/table_pasien.dart'; + +class ResultScreen extends HookConsumerWidget { + const ResultScreen({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final keyword = useState(""); + final keywordCtr = useTextEditingController(text: ""); + final auth = ref.watch(authProvider); + final suratJalanTab = ref.watch(suratJalanProvider); + final rowsPerPage = useState(10); + final searchLoading = useState(false); + final dataStart = useState(1); + final dataEnd = useState(1); + final currPage = useState(1); + final sjList = useState( + SearchSuratJalanModel(suratJalan: [], total: "0", totalPage: 0)); + + onRefresh() { + ref.read(SearchResultProvider.notifier).getData( + ed: "", + sd: "", + token: auth?.token ?? "", + page: currPage.value.toString(), + keyword: keyword.value, + rpp: rowsPerPage.value.toString(), + company_id: auth?.mUserMCompanyID ?? ""); + } + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final debouncer = Debouncer(milliseconds: 100); + debouncer.run(() { + onRefresh(); + }); + }); + return () {}; + }, [currPage.value, suratJalanTab]); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final debouncer = Debouncer(milliseconds: 100); + currPage.value = 1; + debouncer.run(() { + ref.read(SearchResultProvider.notifier).getData( + ed: "", + sd: "", + token: auth?.token ?? "", + page: "1", + keyword: keyword.value, + rpp: rowsPerPage.value.toString(), + company_id: auth?.mUserMCompanyID ?? ""); + }); + }); + return () {}; + }, [ + keyword.value, + rowsPerPage.value, + ]); + + ref.listen( + SearchResultProvider, + (pref, next) { + if (next is SearchResultStateInit) { + searchLoading.value = true; + } else if (next is SearchResultStateLoading) { + searchLoading.value = true; + } else if (next is SearchResultStateError) { + searchLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is SearchResultStateDone) { + sjList.value = next.model; + // print(jsonEncode(next.model)); + + dataStart.value = + (currPage.value * rowsPerPage.value) - rowsPerPage.value + 1; + dataEnd.value = currPage.value == next.model.totalPage + ? int.parse(next.model.total ?? "0") + : dataStart.value + rowsPerPage.value - 1; + + searchLoading.value = false; + } + }, + ); + return Material( + child: Column(children: [ + Header(), + Padding( + padding: const EdgeInsets.all(32.0), + child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 1079), + height: Constant.getActualY(context: context, y: 40), + child: Text('Hasil', + style: Constant.h3_400(context: context) + .copyWith(color: Constant.textBlack)), + ), + SizedBox(height: Constant.getActualY(context: context, y: 42)), + Container( + alignment: Alignment.centerLeft, + width: Constant.getActualX(context: context, x: 409), + child: CustomTextField( + onChange: (value) { + keyword.value = value; + }, + // controller: searchCtr, + isPassword: false, + hintText: "Cari Nomor Surat Jalan", + labelText: "Cari", + suffixIcon: Icon(EvaIcons.search), + ), + ), + SizedBox(height: Constant.getActualY(context: context, y: 24)), + Material( + elevation: 5, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8)), + child: Column(children: [ + Row( + children: [ + Expanded( + flex: 2, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + height: + Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 24, right: 24), + child: Text('Tanggal', + overflow: TextOverflow.ellipsis, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + )), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 24, right: 24), + child: Text('Nomor Surat Jalan', + overflow: TextOverflow.ellipsis, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 3, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 24, right: 24), + child: Text('Staff PIC', + overflow: TextOverflow.ellipsis, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 24, right: 24), + child: Text('New', + overflow: TextOverflow.ellipsis, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 24, right: 24), + child: Text('Send', + overflow: TextOverflow.ellipsis, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 24, right: 24), + child: Text('Receive', + overflow: TextOverflow.ellipsis, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + ], + ), + Container( + color: Colors.white, + height: Constant.getActualY(context: context, y: 500), + child: searchLoading.value + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 40)) + : ListView( + children: sjList.value.suratJalan! + .map( + (e) => TextButton( + onPressed: () { + ref.read(suratJalanID.notifier).state = + e.id ?? ""; + + ref + .read(suratJalanNumber.notifier) + .state = e.orderNumber ?? ""; + ref + .read(currentMenuProvider.notifier) + .state = 7; + }, + style: TextButton.styleFrom( + foregroundColor: Constant.primaryBlue), + child: Row( + children: [ + Expanded( + flex: 2, + child: Padding( + padding: + const EdgeInsets.all(8.0), + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: + const EdgeInsets.only( + top: 12, + bottom: 12, + left: 24, + right: 24), + child: Text(e.date ?? "", + overflow: + TextOverflow.ellipsis, + style: Constant.body3_600( + context: context) + .copyWith( + color: Colors + .black)), + ), + ), + )), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 24, + right: 24), + child: Text(e.orderNumber ?? "", + overflow: + TextOverflow.ellipsis, + style: Constant.body3_600( + context: context) + .copyWith( + color: Colors.black)), + ), + ), + ), + Expanded( + flex: 3, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 24, + right: 24), + child: Text(e.pic ?? "", + overflow: + TextOverflow.ellipsis, + style: Constant.body3_600( + context: context) + .copyWith( + color: Colors.black)), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 24, + right: 24), + child: e.status == "N" || + e.status == "S" || + e.status == "D" + ? Icon( + EvaIcons.checkmarkCircle2, + size: 24, + color: Constant.green, + ) + : null, + ), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 24, + right: 24), + child: Row( + children: [ + if (e.status == "S" || + e.status == "D") + Icon( + EvaIcons.checkmarkCircle2, + size: 24, + color: Constant.green, + ), + if (e.status == "X") + Icon( + EvaIcons.closeCircle, + size: 24, + color: Colors.red, + ), + ], + ), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 24, + right: 24), + child: e.status == "D" + ? Icon( + EvaIcons.checkmarkCircle2, + size: 24, + color: Constant.green, + ) + : null, + ), + ), + ), + ], + ), + ), + ) + .toList(), + ), + ), + Divider(), + SizedBox( + // color: Colors.white, + height: Constant.getActualY(context: context, y: 60), + child: Row( + children: [ + Spacer(), + Text( + "Rows per page :", + style: Constant.body3_400(context: context), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + DropdownButton( + value: rowsPerPage.value, + dropdownColor: Colors.white, + elevation: 0, + style: Constant.body3_400(context: context) + .copyWith(color: Colors.black), + underline: Container(), + icon: Icon(Icons.keyboard_arrow_down_rounded), + focusColor: Colors.white, + items: [ + DropdownMenuItem( + value: 10, + child: Text("10"), + ), + DropdownMenuItem( + value: 15, + child: Text("15"), + ), + DropdownMenuItem( + value: 20, + child: Text("20"), + ), + DropdownMenuItem( + value: 30, + child: Text("30"), + ), + DropdownMenuItem( + value: 40, + child: Text("40"), + ), + DropdownMenuItem( + value: 50, + child: Text("50"), + ), + ], + onChanged: (value) { + rowsPerPage.value = value!; + }), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + Text( + "${dataStart.value} - ${dataEnd.value} of ${sjList.value.total}", + style: Constant.body3_400(context: context), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + IconButton( + iconSize: 20, + onPressed: currPage.value > 1 + ? () { + currPage.value = currPage.value - 1; + } + : null, + icon: Icon(Icons.arrow_back_ios_new_rounded)), + SizedBox( + width: Constant.getActualX(context: context, x: 10), + ), + IconButton( + iconSize: 20, + onPressed: + currPage.value < sjList.value.totalPage!.toInt() + ? () { + currPage.value = currPage.value + 1; + } + : null, + icon: Icon(Icons.arrow_forward_ios_outlined)), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ) + ], + ), + ) + ])) + ])) + ])); + } +} diff --git a/lib/screen/result/search_result_provider.dart b/lib/screen/result/search_result_provider.dart new file mode 100644 index 0000000..582872f --- /dev/null +++ b/lib/screen/result/search_result_provider.dart @@ -0,0 +1,80 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/surat_jalan_model.dart'; +import 'package:mitra_corporate/repository/surat_jalan_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class SearchResultState extends Equatable { + final DateTime date; + const SearchResultState(this.date); + @override + List get props => [date]; +} + +class SearchResultStateInit extends SearchResultState { + SearchResultStateInit() : super(DateTime.now()); +} + +class SearchResultStateLoading extends SearchResultState { + SearchResultStateLoading() : super(DateTime.now()); +} + +class SearchResultStateError extends SearchResultState { + final String message; + SearchResultStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class SearchResultStateDone extends SearchResultState { + final SearchSuratJalanModel model; + SearchResultStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class SearchResultNotifier extends StateNotifier { + final Ref ref; + SearchResultNotifier({ + required this.ref, + }) : super(SearchResultStateInit()); + + void getData({ + required String token, + required String company_id, + required String rpp, + required String keyword, + required String page, + required String sd, + required String ed, + }) async { + try { + state = SearchResultStateLoading(); + final dio = ref.read(dioProvider); + final resp = await SuratJalanRepository(dio: dio).getSuratJalan( + dateType: "1", + token: token, + company_id: company_id, + rpp: rpp, + keyword: keyword, + endDate: ed, + startDate: sd, + page: page); + state = SearchResultStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = SearchResultStateError(message: e.message.toString()); + } else { + state = SearchResultStateError(message: e.toString()); + } + } + } +} + +//provider +final SearchResultProvider = + StateNotifierProvider( + (ref) => SearchResultNotifier(ref: ref)); diff --git a/lib/screen/splash_screen.dart/splash_screen.dart b/lib/screen/splash_screen.dart/splash_screen.dart new file mode 100644 index 0000000..83dd053 --- /dev/null +++ b/lib/screen/splash_screen.dart/splash_screen.dart @@ -0,0 +1,53 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/app/constant.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../app/route.dart'; +import '../../model/auth_model.dart'; +import '../../provider/auth_provider.dart'; + +class SplashScreen extends HookConsumerWidget { + const SplashScreen({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + getPref() async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + final String? getData = prefs.getString(Constant.tokenName); + // print(getData); + if (getData != null) { + Timer(Duration(seconds: 1), () { + ref.read(authProvider.notifier).state = + AuthModel.fromJson(jsonDecode(getData)); + Navigator.pushNamed(context, homeRoute); + }); + } else { + Timer(Duration(seconds: 1), () { + Navigator.pushNamed(context, loginRoute); + }); + } + } + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + // FlutterNativeSplash.remove(); + getPref(); + }); + return () {}; + }, []); + return Container( + color: Colors.white, + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: Center( + child: LoadingAnimationWidget.staggeredDotsWave( + color: Constant.primaryRed, size: 80), + ), + ); + } +} diff --git a/lib/screen/surat_jalan/cancel_surat_jalan_provider.dart b/lib/screen/surat_jalan/cancel_surat_jalan_provider.dart new file mode 100644 index 0000000..843a358 --- /dev/null +++ b/lib/screen/surat_jalan/cancel_surat_jalan_provider.dart @@ -0,0 +1,65 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'package:mitra_corporate/repository/surat_jalan_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class CancelSuratJalanState extends Equatable { + final DateTime date; + const CancelSuratJalanState(this.date); + @override + List get props => [date]; +} + +class CancelSuratJalanStateInit extends CancelSuratJalanState { + CancelSuratJalanStateInit() : super(DateTime.now()); +} + +class CancelSuratJalanStateLoading extends CancelSuratJalanState { + CancelSuratJalanStateLoading() : super(DateTime.now()); +} + +class CancelSuratJalanStateError extends CancelSuratJalanState { + final String message; + CancelSuratJalanStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class CancelSuratJalanStateDone extends CancelSuratJalanState { + final String number; + CancelSuratJalanStateDone({ + required this.number, + }) : super(DateTime.now()); +} + +//notifier +class CancelSuratJalanNotifier extends StateNotifier { + final Ref ref; + CancelSuratJalanNotifier({ + required this.ref, + }) : super(CancelSuratJalanStateInit()); + + void cancel({required String token, required String deliveryID}) async { + try { + state = CancelSuratJalanStateLoading(); + final dio = ref.read(dioProvider); + final resp = await SuratJalanRepository(dio: dio) + .cancelSuratJalan(token: token, deliveryID: deliveryID); + state = CancelSuratJalanStateDone(number: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = CancelSuratJalanStateError(message: e.message.toString()); + } else { + state = CancelSuratJalanStateError(message: e.toString()); + } + } + } +} + +//provider +final CancelSuratJalanProvider = + StateNotifierProvider( + (ref) => CancelSuratJalanNotifier(ref: ref)); diff --git a/lib/screen/surat_jalan/create_surat_jalan_provider.dart b/lib/screen/surat_jalan/create_surat_jalan_provider.dart new file mode 100644 index 0000000..44e7ee9 --- /dev/null +++ b/lib/screen/surat_jalan/create_surat_jalan_provider.dart @@ -0,0 +1,65 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/create_surat_jalan_model.dart'; +import 'package:mitra_corporate/repository/surat_jalan_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class CreateSuratJalanState extends Equatable { + final DateTime date; + const CreateSuratJalanState(this.date); + @override + List get props => [date]; +} + +class CreateSuratJalanStateInit extends CreateSuratJalanState { + CreateSuratJalanStateInit() : super(DateTime.now()); +} + +class CreateSuratJalanStateLoading extends CreateSuratJalanState { + CreateSuratJalanStateLoading() : super(DateTime.now()); +} + +class CreateSuratJalanStateError extends CreateSuratJalanState { + final String message; + CreateSuratJalanStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class CreateSuratJalanStateDone extends CreateSuratJalanState { + final String number; + CreateSuratJalanStateDone({ + required this.number, + }) : super(DateTime.now()); +} + +//notifier +class CreateSuratJalanNotifier extends StateNotifier { + final Ref ref; + CreateSuratJalanNotifier({ + required this.ref, + }) : super(CreateSuratJalanStateInit()); + + void addOrder({required CreateSuratJalanModel prm}) async { + try { + state = CreateSuratJalanStateLoading(); + final dio = ref.read(dioProvider); + final resp = + await SuratJalanRepository(dio: dio).createSuratJalan(prm: prm); + state = CreateSuratJalanStateDone(number: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = CreateSuratJalanStateError(message: e.message.toString()); + } else { + state = CreateSuratJalanStateError(message: e.toString()); + } + } + } +} + +//provider +final CreateSuratJalanProvider = + StateNotifierProvider( + (ref) => CreateSuratJalanNotifier(ref: ref)); diff --git a/lib/screen/surat_jalan/dialog_cancel.dart b/lib/screen/surat_jalan/dialog_cancel.dart new file mode 100644 index 0000000..1b253e8 --- /dev/null +++ b/lib/screen/surat_jalan/dialog_cancel.dart @@ -0,0 +1,182 @@ +import 'package:flutter/material.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; + +class DialogCancelSuratJalan extends StatelessWidget { + const DialogCancelSuratJalan( + {super.key, + required this.date, + required this.deliveryNumber, + required this.cancel, + required this.deliveryID, + required this.loading, + required this.pic}); + final String date; + final String pic; + final String deliveryNumber; + final Function cancel; + final String deliveryID; + final bool loading; + + @override + Widget build(BuildContext context) { + return AlertDialog( + titlePadding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 24)), + actionsPadding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 24)), + contentPadding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 24)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8))), + title: Text( + 'Konfirmasi Pembatalan', + style: Constant.h4_600(context: context), + ), + content: Container( + constraints: BoxConstraints( + minHeight: Constant.getActualY(context: context, y: 100), + maxHeight: Constant.getActualY(context: context, y: 362), + ), + height: Constant.getActualY(context: context, y: 240), + child: Column( + children: [ + Text( + 'Anda yakin membatalkan surat jalan berikut ?', + style: Constant.body1_600(context: context), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 24), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 416), + child: Card( + elevation: 1, + shape: RoundedRectangleBorder( + side: BorderSide(color: Constant.grey_200), + borderRadius: BorderRadius.circular(12)), + child: Container( + padding: EdgeInsets.symmetric( + horizontal: + Constant.getActualX(context: context, x: 24), + vertical: + Constant.getActualY(context: context, y: 24)), + child: Column( + children: [ + Row( + children: [ + Expanded( + flex: 5, + child: Text( + "Tanggal", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + ), + Expanded( + flex: 7, + child: Text( + date, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack), + ), + ) + ], + ), + SizedBox( + height: + Constant.getActualY(context: context, y: 12), + ), + Row( + children: [ + Expanded( + flex: 5, + child: Text( + "Nomor Surat Jalan", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + ), + Expanded( + flex: 7, + child: Text( + deliveryNumber, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack), + ), + ) + ], + ), + SizedBox( + height: + Constant.getActualY(context: context, y: 12), + ), + Row( + children: [ + Expanded( + flex: 5, + child: Text( + "Staff PIC", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + ), + Expanded( + flex: 7, + child: Text( + pic, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack), + ), + ) + ], + ), + ], + )), + ), + ) + ], + )), + actions: [ + OutlinedButton( + style: OutlinedButton.styleFrom( + surfaceTintColor: Constant.primaryBlue, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: !loading ? () => Navigator.pop(context, 'Batal') : null, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: loading + ? LoadingAnimationWidget.staggeredDotsWave( + color: Constant.primaryBlue, size: 20) + : Text('Batal', + style: Constant.button_medium(context: context) + .copyWith(color: Constant.textBlack)), + ), + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: !loading ? () => cancel(deliveryID) : null, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: loading + ? LoadingAnimationWidget.staggeredDotsWave( + color: Colors.white, size: 20) + : Text( + 'Yakin', + style: Constant.button_medium(context: context) + .copyWith(color: Colors.white), + ), + ), + ), + ], + ); + } +} diff --git a/lib/screen/surat_jalan/dialog_send.dart b/lib/screen/surat_jalan/dialog_send.dart new file mode 100644 index 0000000..56fd9eb --- /dev/null +++ b/lib/screen/surat_jalan/dialog_send.dart @@ -0,0 +1,182 @@ +import 'package:flutter/material.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; + +class DialogConfirmSuratJalan extends StatelessWidget { + const DialogConfirmSuratJalan( + {super.key, + required this.date, + required this.deliveryNumber, + required this.sendFunction, + required this.deliveryID, + required this.loading, + required this.pic}); + final String date; + final String pic; + final String deliveryNumber; + final Function sendFunction; + final String deliveryID; + final bool loading; + + @override + Widget build(BuildContext context) { + return AlertDialog( + titlePadding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 24)), + actionsPadding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 24)), + contentPadding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 24)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8))), + title: Text( + 'Konfirmasi Surat Jalan', + style: Constant.h4_600(context: context), + ), + content: Container( + constraints: BoxConstraints( + minHeight: Constant.getActualY(context: context, y: 100), + maxHeight: Constant.getActualY(context: context, y: 362), + ), + height: Constant.getActualY(context: context, y: 240), + child: Column( + children: [ + Text( + 'Anda yakin mengirimkan surat jalan berikut ?', + style: Constant.body1_600(context: context), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 24), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 416), + child: Card( + elevation: 1, + shape: RoundedRectangleBorder( + side: BorderSide(color: Constant.grey_200), + borderRadius: BorderRadius.circular(12)), + child: Container( + padding: EdgeInsets.symmetric( + horizontal: + Constant.getActualX(context: context, x: 24), + vertical: + Constant.getActualY(context: context, y: 24)), + child: Column( + children: [ + Row( + children: [ + Expanded( + flex: 5, + child: Text( + "Tanggal", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + ), + Expanded( + flex: 7, + child: Text( + date, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack), + ), + ) + ], + ), + SizedBox( + height: + Constant.getActualY(context: context, y: 12), + ), + Row( + children: [ + Expanded( + flex: 5, + child: Text( + "Nomor Surat Jalan", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + ), + Expanded( + flex: 7, + child: Text( + deliveryNumber, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack), + ), + ) + ], + ), + SizedBox( + height: + Constant.getActualY(context: context, y: 12), + ), + Row( + children: [ + Expanded( + flex: 5, + child: Text( + "Staff PIC", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + ), + Expanded( + flex: 7, + child: Text( + pic, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack), + ), + ) + ], + ), + ], + )), + ), + ) + ], + )), + actions: [ + OutlinedButton( + style: OutlinedButton.styleFrom( + surfaceTintColor: Constant.primaryBlue, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: !loading ? () => Navigator.pop(context, 'Batal') : null, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: loading + ? LoadingAnimationWidget.staggeredDotsWave( + color: Colors.white, size: 20) + : Text('Batal', + style: Constant.button_medium(context: context) + .copyWith(color: Constant.textBlack)), + ), + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Constant.green, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: !loading ? () => sendFunction(deliveryID) : null, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: loading + ? LoadingAnimationWidget.staggeredDotsWave( + color: Colors.white, size: 20) + : Text( + 'Yakin', + style: Constant.button_medium(context: context) + .copyWith(color: Colors.white), + ), + ), + ), + ], + ); + } +} diff --git a/lib/screen/surat_jalan/dialog_send_qr.dart b/lib/screen/surat_jalan/dialog_send_qr.dart new file mode 100644 index 0000000..107756f --- /dev/null +++ b/lib/screen/surat_jalan/dialog_send_qr.dart @@ -0,0 +1,209 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/screen/surat_jalan/send_qr_code_provider.dart'; + +import '../../app/constant.dart'; +import '../../widgets/custom_snackbar_widget.dart'; + +class DialogSendQr extends HookConsumerWidget { + const DialogSendQr( + {super.key, required this.arrOrderID, required this.orderNumber}); + final List arrOrderID; + final String orderNumber; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final auth = ref.watch(authProvider); + + final rb = useState("Y"); + + sendQrCode() { + ref + .read(SendQrCodeProvider.notifier) + .senQrCode(token: auth?.token ?? "", arrOrderID: arrOrderID); + } + + ref.listen( + SendQrCodeProvider, + (pref, next) { + if (next is SendQrCodeStateInit) { + } else if (next is SendQrCodeStateLoading) { + } else if (next is SendQrCodeStateError) { + SanckbarWidget( + context, + "Gagal Menyimpan Data${jsonEncode(next.message)}", + snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is SendQrCodeStateDone) { + Navigator.pop(context); + // print(jsonEncode(next.model)); + + if (next.model != 'OK') { + SanckbarWidget(context, "Gagal Menyimpan Data", snackbarType.error); + } else { + SanckbarWidget( + context, + "Berhasil Menyimpan Data, Qr Code Akan Dikirimkan", + snackbarType.success); + } + } + }, + ); + + return AlertDialog( + contentPadding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 24)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(12))), + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Kirim QR Code', + textAlign: TextAlign.center, + style: Constant.h4_600(context: context) + .copyWith(fontWeight: FontWeight.w700), + ), + IconButton( + onPressed: () { + Navigator.pop(context); + }, + icon: Icon(Icons.close_rounded)) + ], + ), + content: Container( + constraints: BoxConstraints( + minHeight: Constant.getActualY(context: context, y: 100), + maxHeight: Constant.getActualY(context: context, y: 362), + ), + height: Constant.getActualY(context: context, y: 320), + child: Column( + // crossAxisAlignment: CrossAxisAlignment.center, + // mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: Constant.getActualY(context: context, y: 30), + ), + Text( + "Surat Jalan Berhasil Dibuat", + style: Constant.body1_600(context: context) + .copyWith(fontWeight: FontWeight.w600), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + Container( + width: Constant.getActualX(context: context, x: 263), + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 12)), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade300)), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Nomor Surat Jalan", + style: Constant.body3_500(context: context) + .copyWith(color: Constant.textGrey), + ), + Text( + orderNumber, + style: Constant.body3_500(context: context) + .copyWith(fontWeight: FontWeight.w600), + ) + ], + ), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + // width: Constant.getActualX(context: context, x: 48), + child: RadioMenuButton( + style: ButtonStyle( + backgroundColor: WidgetStateColor.resolveWith( + (states) => Colors.white, + ), + shadowColor: WidgetStateColor.resolveWith( + (states) => Colors.white), + overlayColor: WidgetStateColor.resolveWith( + (states) => Colors.white)), + value: 'Y', + groupValue: rb.value, + onChanged: (value) { + if (rb.value == value) { + rb.value = 'N'; + } else { + rb.value = value!; + } + }, + child: Text( + "Kirim Whatsapp QR Code", + style: Constant.body3_400(context: context), + )), + ), + // Text( + // "Kirim Whatsapp QR Code", + // style: Constant.body3_400(context: context), + // ) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 37), + ), + ElevatedButton( + onPressed: rb.value == 'Y' + ? () { + sendQrCode(); + } + : null, + style: ElevatedButton.styleFrom( + minimumSize: Size( + Constant.getActualX(context: context, x: 328), + Constant.getActualY(context: context, y: 64)), + elevation: 5, + backgroundColor: Constant.primaryRed, + shadowColor: Constant.primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + child: Container( + margin: EdgeInsets.symmetric( + vertical: Constant.getActualY(context: context, y: 11)), + child: Text( + "Kirim QR Code", + style: Constant.button_large(context: context) + .copyWith(color: Colors.white), + ), + )), + // SizedBox( + // height: Constant.getActualY(context: context, y: 12), + // ), + // Row( + // children: [ + // Text( + // "Belum menerima QR Code ?", + // style: Constant.caption1_600(context: context) + // .copyWith(color: Constant.textGrey), + // ), + // TextButton( + // onPressed: rb.value == 'Y' ? () {} : null, + // child: Text("Kirim Ulang", + // style: Constant.caption1_600(context: context) + // .copyWith())) + // ], + // ) + ], + )), + ); + } +} diff --git a/lib/screen/surat_jalan/dialog_surat_jalan.dart b/lib/screen/surat_jalan/dialog_surat_jalan.dart new file mode 100644 index 0000000..91aeb23 --- /dev/null +++ b/lib/screen/surat_jalan/dialog_surat_jalan.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/provider/surat_jalan_provider.dart'; +import 'package:mitra_corporate/screen/surat_jalan/form_surat_jalan.dart'; +import 'package:mitra_corporate/screen/surat_jalan/surat_jalan_confirmation.dart'; + +import '../../app/constant.dart'; + +class DialogSuratJalan extends HookConsumerWidget { + const DialogSuratJalan({super.key}); + + // const DialogSuratJalan({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final bacaSuratJalanProvider = ref.watch(suratJalanProvider); + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: SimpleDialog( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(12))), + children: [ + Padding( + padding: EdgeInsets.fromLTRB(40, 20, 40, 20), + child: Column( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 1332), + height: Constant.getActualY(context: context, y: 62), + child: Text( + bacaSuratJalanProvider == 1 + ? 'Pengiriman Order Baru' + : "Konfirmasi", + style: Constant.h2_600(context: context) + .copyWith(color: Constant.textBlack)), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 10)), + + /// + if (bacaSuratJalanProvider == 1) FormSuratJalan(), + if (bacaSuratJalanProvider == 2) SuratJalanConfirmation(), + + /// + ], + ), + ), + ]), + ); + } +} diff --git a/lib/screen/surat_jalan/form_surat_jalan.dart b/lib/screen/surat_jalan/form_surat_jalan.dart new file mode 100644 index 0000000..fac13c1 --- /dev/null +++ b/lib/screen/surat_jalan/form_surat_jalan.dart @@ -0,0 +1,529 @@ +import 'dart:convert'; + +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/intl.dart'; +import 'package:mitra_corporate/model/create_surat_jalan_model.dart'; +import 'package:mitra_corporate/model/delivery_type_model.dart'; +import 'package:mitra_corporate/model/destination_model.dart'; +import 'package:mitra_corporate/model/order_model.dart'; +import 'package:mitra_corporate/model/registration_filter_model.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/screen/surat_jalan/get_order_list_provider.dart'; +import 'package:mitra_corporate/screen/surat_jalan/get_regional_provider.dart'; +import 'package:mitra_corporate/screen/surat_jalan/list_pasien_terpilih.dart'; +import 'package:mitra_corporate/screen/surat_jalan/table_list_pasien.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../provider/surat_jalan_provider.dart'; +import '../../widgets/custom_snackbar_widget.dart'; +import '../../widgets/custom_text_field.dart'; + +class FormSuratJalan extends HookConsumerWidget { + const FormSuratJalan({ + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final tempSuratJalan = ref.watch(createSuratJalanProvider); + final auth = ref.watch(authProvider); + final picCtr = useTextEditingController(text: auth?.mUserUsername ?? ""); + final noRefCtr = + useTextEditingController(text: tempSuratJalan.order?.noRef); + final suhuBoxCtr = + useTextEditingController(text: tempSuratJalan.order?.temperature); + final noResiCtr = + useTextEditingController(text: tempSuratJalan.order?.noResi); + final noteCtr = useTextEditingController(text: tempSuratJalan.order?.note); + final searchPatient = useTextEditingController(text: ""); + final deliveryTypeLoading = useState(false); + final selectedDeliveryType = + useState(DeliveryTypeModel()); + final deliveryTypeList = useState>(List.empty()); + final deliveryTypeKey = useState(1); + final deliveryDate = useTextEditingController( + text: DateFormat('dd-MM-yyyy').format(DateTime.parse( + tempSuratJalan.order?.date ?? DateTime.now().toString()))); + final deliveryState = useState(DateTime.parse( + tempSuratJalan.order?.date ?? DateTime.now().toString())); + final getOrderLoading = useState(false); + final orderList = useState>(List.empty()); + final fullOrderList = useState>(List.empty()); + final selectedOrder = useState>(List.empty()); + final destinationList = useState>(List.empty()); + final selectedDestination = useState(DestinationModel()); + final regionalList = useState>(List.empty()); + final selectedRegional = useState(RegionalModel()); + + final destinationLoading = useState(false); + final regionalLoading = useState(false); + final destinationKey = useState(1000); + final regionalKey = useState(5000); + final branchKey = useState(10000); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + // deliveryState.value = DateTime.parse(tempSuratJalan.order?.date ?? ''); + // ref + // .read(GetDeliveryTypeProvider.notifier) + // .getData(token: auth?.token ?? ""); + ref.read(GetOrderListProvider.notifier).getData( + token: auth?.token ?? "", + company_id: auth?.mUserMCompanyID ?? "", + regional_id: auth?.MUserSRegionalID ?? "0"); + ref + .read(GetRegionalProvider.notifier) + .getRegional(token: auth?.token ?? ""); + }); + return () {}; + }, []); + + // ref.listen( + // GetDeliveryTypeProvider, + // (previous, next) { + // if (next is GetDeliveryTypeStateInit) { + // deliveryTypeLoading.value = true; + // } else if (next is GetDeliveryTypeStateLoading) { + // deliveryTypeLoading.value = true; + // } else if (next is GetDeliveryTypeStateError) { + // SanckbarWidget(context, next.message, snackbarType.error); + // deliveryTypeLoading.value = false; + // } else if (next is GetDeliveryTypeStateDone) { + // // print(jsonEncode(next.model)); + // deliveryTypeList.value = next.model; + // next.model.forEach((element) { + // if (element.id == tempSuratJalan.order?.typeId) { + // selectedDeliveryType.value = element; + // } + // }); + // deliveryTypeKey.value = deliveryTypeKey.value + 1; + + // deliveryTypeLoading.value = false; + // } + // }, + // ); + ref.listen( + GetOrderListProvider, + (previous, next) { + if (next is GetOrderListStateInit) { + getOrderLoading.value = true; + } else if (next is GetOrderListStateLoading) { + getOrderLoading.value = true; + } else if (next is GetOrderListStateError) { + SanckbarWidget(context, next.message, snackbarType.error); + getOrderLoading.value = false; + Constant.autoLogout(context: context, msg: next.message); + } else if (next is GetOrderListStateDone) { + // print(jsonEncode(next.model)); + var a = next.model; + selectedOrder.value = tempSuratJalan.orderDetail ?? []; + for (var element in selectedOrder.value) { + a.removeWhere((i) => element.orderId == i.orderId); + } + orderList.value = a; + fullOrderList.value = a; + + getOrderLoading.value = false; + } + }, + ); + + ref.listen( + GetRegionalProvider, + (previous, next) { + if (next is GetRegionalStateInit) { + regionalLoading.value = true; + } else if (next is GetRegionalStateLoading) { + regionalLoading.value = true; + } else if (next is GetRegionalStateError) { + SanckbarWidget(context, next.message, snackbarType.error); + regionalLoading.value = false; + Constant.autoLogout(context: context, msg: next.message); + } else if (next is GetRegionalStateDone) { + // print(jsonEncode(next.model)); + regionalList.value = next.model; + for (var element in next.model) { + if (element.regionalId == tempSuratJalan.order?.regionalId) { + selectedRegional.value = element; + destinationList.value = element.branch ?? []; + element.branch?.forEach((i) { + if (i.branchId == tempSuratJalan.order?.destinationId) { + selectedDestination.value = i; + } + }); + } + } + regionalKey.value = regionalKey.value + 1; + branchKey.value = branchKey.value + 1; + destinationKey.value = destinationKey.value + 1; + + regionalLoading.value = false; + } + }, + ); + selectOrder(OrderModel e) { + List temp = orderList.value; + List fullTemp = fullOrderList.value; + List tempSlct = selectedOrder.value.toList(growable: true); + temp.removeWhere((element) => element.orderId == e.orderId); + fullTemp.removeWhere((element) => element.orderId == e.orderId); + tempSlct.add(e); + + orderList.value = temp; + fullOrderList.value = fullTemp; + selectedOrder.value = tempSlct; + } + + unSelectOrder(OrderModel e) { + List temp = orderList.value.toList(growable: true); + List tempSlct = selectedOrder.value.toList(growable: true); + List fullTemp = fullOrderList.value; + tempSlct.removeWhere((element) => element.orderId == e.orderId); + temp.add(e); + fullTemp.add(e); + + orderList.value = temp; + fullOrderList.value = fullTemp; + selectedOrder.value = tempSlct; + } + + return Column( + children: [ + Row( + children: [ + // Container( + // width: Constant.getActualX(context: context, x: 382), + // // height: Constant.getActualY(context: context, y: 56), + // child: DropdownMenu( + // menuHeight: Constant.getActualY(context: context, y: 300), + // key: ValueKey(regionalKey.value), + // initialSelection: selectedRegional.value, + // trailingIcon: regionalLoading.value + // ? LoadingAnimationWidget.discreteCircle( + // color: Constant.primaryRed, size: 20) + // : null, + // width: Constant.getActualX(context: context, x: 382), + // hintText: "Regional", + // textStyle: Constant.body2_400(context: context), + // inputDecorationTheme: InputDecorationTheme( + // border: OutlineInputBorder( + // borderSide: BorderSide(color: Constant.primaryRed), + // borderRadius: BorderRadius.circular(8), + // )), + // label: const Text( + // "Regional", + // maxLines: 1, + // overflow: TextOverflow.ellipsis, + // ), + // onSelected: (value) { + // // onSelectSaluation(value!); + // if (value!.regionalId != + // selectedRegional.value.regionalId) { + // print("change"); + // selectedRegional.value = value; + // selectedDestination.value = DestinationModel(); + // destinationList.value = value.branch ?? []; + // } + // branchKey.value = branchKey.value + 1; + // }, + // dropdownMenuEntries: regionalList.value + // .map>((e) => + // DropdownMenuEntry( + // value: e, label: e.regionalName ?? "")) + // .toList()), + // ), + // SizedBox( + // width: Constant.getActualX(context: context, x: 24), + // ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 24), + ), + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 318), + // height: Constant.getActualY(context: context, y: 56), + child: DropdownMenu( + menuHeight: Constant.getActualY(context: context, y: 300), + key: ValueKey(branchKey.value), + initialSelection: selectedDestination.value, + trailingIcon: regionalLoading.value + ? LoadingAnimationWidget.discreteCircle( + color: Constant.primaryRed, size: 20) + : null, + width: Constant.getActualX(context: context, x: 318), + hintText: "Cabang", + textStyle: Constant.body2_400(context: context), + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryRed), + borderRadius: BorderRadius.circular(8), + )), + label: const Text( + "Cabang", + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + onSelected: (value) { + // onSelectSaluation(value!); + selectedDestination.value = value!; + }, + dropdownMenuEntries: destinationList.value + .map>((e) => + DropdownMenuEntry( + value: e, label: e.branchName ?? "")) + .toList()), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 318), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + controller: picCtr, + hintText: 'Staf PIC', + labelText: "Staf PIC", + disable: false, + ), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 318), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + controller: noRefCtr, + hintText: 'Nomor Referensi Surat', + labelText: "Nomor Referensi Surat"), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 165), + // height: Constant.getActualY(context: context, y: 56), + child: TextField( + controller: deliveryDate, + style: Constant.body2_400(context: context), + decoration: InputDecoration( + suffixIcon: Icon(EvaIcons.calendarOutline), + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + ), + labelText: "Tanggal Kedatangan" //label text of field + ), + readOnly: true, + onTap: () async { + DateTime? pickedDate = await showDatePicker( + // locale: Locale("ID"), + cancelText: "Batal", + confirmText: "Simpan", + context: context, + initialDate: deliveryState.value, + firstDate: DateTime(1800), + initialEntryMode: DatePickerEntryMode.calendarOnly, + + //DateTime.now() - not to allow to choose before today. + lastDate: DateTime(2100)); + + if (pickedDate != null) { + print( + pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000 + String formattedDate = + DateFormat('dd-MM-yyyy').format(pickedDate); + print( + formattedDate); //formatted date output using intl package => 2021-03-16 + deliveryDate.text = + formattedDate; //set output date to TextField value. + deliveryState.value = pickedDate; + } else {} + }, + ), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 24), + ), + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 1194), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + controller: noteCtr, + hintText: 'Catatan', + labelText: 'Catatan'), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 24), + ), + Row( + children: [ + SizedBox( + width: Constant.getActualX(context: context, x: 585), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + onChange: (value) { + List temp = fullOrderList.value.toSet().toList(); + + // List fullOrderList = orderList.value.toSet().toList(); + var searched = temp.where((element) => + element.patientName! + .toLowerCase() + .contains(value.toString().toLowerCase()) || + element.orderNumber! + .toLowerCase() + .contains(value.toString().toLowerCase())); + if (value.isNotEmpty) { + orderList.value = searched.toList(); + } else { + orderList.value = temp; + } + // jsonEncode(searched.toString()); + }, + controller: searchPatient, + hintText: 'Nama Pasien, Nomor Registrasi', + labelText: 'Nama Pasien, Nomor Registrasi', + suffixIcon: Icon(EvaIcons.search), + ), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + Container( + width: Constant.getActualX(context: context, x: 585), + // height: Constant.getActualY(context: context, y: 56), + alignment: Alignment.centerLeft, + child: Text('List Pasien Terpilih', + style: Constant.h4_600(context: context) + .copyWith(color: Constant.textBlack)), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + Row( + children: [ + TableListPasien( + orders: orderList.value, + loading: getOrderLoading.value, + selected: selectOrder, + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + ListPasienTerpilih( + orders: selectedOrder.value, + loading: getOrderLoading.value, + unselect: unSelectOrder, + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( + height: Constant.getActualY(context: context, y: 45), + child: OutlinedButton( + style: OutlinedButton.styleFrom( + backgroundColor: Colors.white, + side: BorderSide(color: Colors.grey, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: () { + ref.read(createSuratJalanProvider.notifier).state = + CreateSuratJalanModel(); + Navigator.pop(context); + }, + child: Text('Batal', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack))), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + Container( + height: Constant.getActualY(context: context, y: 45), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: Constant.green), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Constant.green, + side: BorderSide(color: Constant.green, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + onPressed: () { + if (selectedOrder.value.isEmpty) { + SanckbarWidget(context, "Pilih salah satu order", + snackbarType.warning); + return; + } + + if (selectedDestination.value.branchId == null) { + SanckbarWidget(context, "Pilih salah satu tujuan", + snackbarType.warning); + return; + } + if (selectedRegional.value.regionalId == null) { + SanckbarWidget(context, "Pilih salah satu regional", + snackbarType.warning); + return; + } + ref.read(createSuratJalanProvider.notifier).state = + CreateSuratJalanModel( + order: Order( + branchCode: + selectedDestination.value.branchCode, + companyId: auth?.mUserMCompanyID, + date: deliveryState.value.toString(), + regionalId: selectedRegional.value.regionalId, + regionalName: + selectedRegional.value.regionalName, + destinationName: + selectedDestination.value.branchName, + destinationId: + selectedDestination.value.branchId, + noRef: noRefCtr.text, + noResi: noResiCtr.text, + note: noteCtr.text, + staffId: auth?.mUserID, + typeName: selectedDeliveryType.value.name, + temperature: suhuBoxCtr.text, + typeId: selectedDeliveryType.value.id), + orderDetail: selectedOrder.value, + token: auth?.token); + ref.read(suratJalanProvider.state).update((state) => 2); + print(jsonEncode(tempSuratJalan)); + // kamu liat ntar value nya jadi berapa + // print(); + }, + child: Text('Selanjutnya', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textWhite))), + ), + ], + ) + ], + ); + } +} diff --git a/lib/screen/surat_jalan/get_delivery_type_provider.dart b/lib/screen/surat_jalan/get_delivery_type_provider.dart new file mode 100644 index 0000000..ef1c2ae --- /dev/null +++ b/lib/screen/surat_jalan/get_delivery_type_provider.dart @@ -0,0 +1,65 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/delivery_type_model.dart'; +import 'package:mitra_corporate/repository/surat_jalan_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class GetDeliveryTypeState extends Equatable { + final DateTime date; + const GetDeliveryTypeState(this.date); + @override + List get props => [date]; +} + +class GetDeliveryTypeStateInit extends GetDeliveryTypeState { + GetDeliveryTypeStateInit() : super(DateTime.now()); +} + +class GetDeliveryTypeStateLoading extends GetDeliveryTypeState { + GetDeliveryTypeStateLoading() : super(DateTime.now()); +} + +class GetDeliveryTypeStateError extends GetDeliveryTypeState { + final String message; + GetDeliveryTypeStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class GetDeliveryTypeStateDone extends GetDeliveryTypeState { + final List model; + GetDeliveryTypeStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class GetDeliveryTypeNotifier extends StateNotifier { + final Ref ref; + GetDeliveryTypeNotifier({ + required this.ref, + }) : super(GetDeliveryTypeStateInit()); + + void getData({required String token}) async { + try { + state = GetDeliveryTypeStateLoading(); + final dio = ref.read(dioProvider); + final resp = + await SuratJalanRepository(dio: dio).getDeliveryType(token: token); + state = GetDeliveryTypeStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = GetDeliveryTypeStateError(message: e.message.toString()); + } else { + state = GetDeliveryTypeStateError(message: e.toString()); + } + } + } +} + +//provider +final GetDeliveryTypeProvider = + StateNotifierProvider( + (ref) => GetDeliveryTypeNotifier(ref: ref)); diff --git a/lib/screen/surat_jalan/get_destination_provider.dart b/lib/screen/surat_jalan/get_destination_provider.dart new file mode 100644 index 0000000..c27cfa8 --- /dev/null +++ b/lib/screen/surat_jalan/get_destination_provider.dart @@ -0,0 +1,65 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/repository/surat_jalan_repository.dart'; + +import '../../model/destination_model.dart'; +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class GetDestinationState extends Equatable { + final DateTime date; + const GetDestinationState(this.date); + @override + List get props => [date]; +} + +class GetDestinationStateInit extends GetDestinationState { + GetDestinationStateInit() : super(DateTime.now()); +} + +class GetDestinationStateLoading extends GetDestinationState { + GetDestinationStateLoading() : super(DateTime.now()); +} + +class GetDestinationStateError extends GetDestinationState { + final String message; + GetDestinationStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class GetDestinationStateDone extends GetDestinationState { + final List model; + GetDestinationStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class GetDestinationNotifier extends StateNotifier { + final Ref ref; + GetDestinationNotifier({ + required this.ref, + }) : super(GetDestinationStateInit()); + + void getData({required String token}) async { + try { + state = GetDestinationStateLoading(); + final dio = ref.read(dioProvider); + final resp = + await SuratJalanRepository(dio: dio).getDestination(token: token); + state = GetDestinationStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = GetDestinationStateError(message: e.message.toString()); + } else { + state = GetDestinationStateError(message: e.toString()); + } + } + } +} + +//provider +final GetDestinationProvider = + StateNotifierProvider( + (ref) => GetDestinationNotifier(ref: ref)); diff --git a/lib/screen/surat_jalan/get_order_list_provider.dart b/lib/screen/surat_jalan/get_order_list_provider.dart new file mode 100644 index 0000000..c70d9ab --- /dev/null +++ b/lib/screen/surat_jalan/get_order_list_provider.dart @@ -0,0 +1,68 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/order_model.dart'; +import 'package:mitra_corporate/repository/surat_jalan_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class GetOrderListState extends Equatable { + final DateTime date; + const GetOrderListState(this.date); + @override + List get props => [date]; +} + +class GetOrderListStateInit extends GetOrderListState { + GetOrderListStateInit() : super(DateTime.now()); +} + +class GetOrderListStateLoading extends GetOrderListState { + GetOrderListStateLoading() : super(DateTime.now()); +} + +class GetOrderListStateError extends GetOrderListState { + final String message; + GetOrderListStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class GetOrderListStateDone extends GetOrderListState { + final List model; + GetOrderListStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class GetOrderListNotifier extends StateNotifier { + final Ref ref; + GetOrderListNotifier({ + required this.ref, + }) : super(GetOrderListStateInit()); + + void getData( + {required String token, + required String company_id, + required String regional_id}) async { + try { + state = GetOrderListStateLoading(); + final dio = ref.read(dioProvider); + final resp = await SuratJalanRepository(dio: dio).getOrder( + token: token, company_id: company_id, regional_id: regional_id); + state = GetOrderListStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = GetOrderListStateError(message: e.message.toString()); + } else { + state = GetOrderListStateError(message: e.toString()); + } + } + } +} + +//provider +final GetOrderListProvider = + StateNotifierProvider( + (ref) => GetOrderListNotifier(ref: ref)); diff --git a/lib/screen/surat_jalan/get_regional_provider.dart b/lib/screen/surat_jalan/get_regional_provider.dart new file mode 100644 index 0000000..2d3bf30 --- /dev/null +++ b/lib/screen/surat_jalan/get_regional_provider.dart @@ -0,0 +1,66 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/registration_filter_model.dart'; + +import 'package:mitra_corporate/repository/surat_jalan_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class GetRegionalState extends Equatable { + final DateTime date; + const GetRegionalState(this.date); + @override + List get props => [date]; +} + +class GetRegionalStateInit extends GetRegionalState { + GetRegionalStateInit() : super(DateTime.now()); +} + +class GetRegionalStateLoading extends GetRegionalState { + GetRegionalStateLoading() : super(DateTime.now()); +} + +class GetRegionalStateError extends GetRegionalState { + final String message; + GetRegionalStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class GetRegionalStateDone extends GetRegionalState { + final List model; + GetRegionalStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class GetRegionalNotifier extends StateNotifier { + final Ref ref; + GetRegionalNotifier({ + required this.ref, + }) : super(GetRegionalStateInit()); + + void getRegional({required String token}) async { + try { + state = GetRegionalStateLoading(); + final dio = ref.read(dioProvider); + final resp = + await SuratJalanRepository(dio: dio).getRegional(token: token); + state = GetRegionalStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = GetRegionalStateError(message: e.message.toString()); + } else { + state = GetRegionalStateError(message: e.toString()); + } + } + } +} + +//provider +final GetRegionalProvider = + StateNotifierProvider( + (ref) => GetRegionalNotifier(ref: ref)); diff --git a/lib/screen/surat_jalan/get_surat_jalan_detail_provider.dart b/lib/screen/surat_jalan/get_surat_jalan_detail_provider.dart new file mode 100644 index 0000000..35d85b0 --- /dev/null +++ b/lib/screen/surat_jalan/get_surat_jalan_detail_provider.dart @@ -0,0 +1,66 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/surat_jalan_detail_model.dart'; +import 'package:mitra_corporate/repository/surat_jalan_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class GetSuratJalanDetailState extends Equatable { + final DateTime date; + const GetSuratJalanDetailState(this.date); + @override + List get props => [date]; +} + +class GetSuratJalanDetailStateInit extends GetSuratJalanDetailState { + GetSuratJalanDetailStateInit() : super(DateTime.now()); +} + +class GetSuratJalanDetailStateLoading extends GetSuratJalanDetailState { + GetSuratJalanDetailStateLoading() : super(DateTime.now()); +} + +class GetSuratJalanDetailStateError extends GetSuratJalanDetailState { + final String message; + GetSuratJalanDetailStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class GetSuratJalanDetailStateDone extends GetSuratJalanDetailState { + final List model; + GetSuratJalanDetailStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class GetSuratJalanDetailNotifier + extends StateNotifier { + final Ref ref; + GetSuratJalanDetailNotifier({ + required this.ref, + }) : super(GetSuratJalanDetailStateInit()); + + void getData({required String token, required String deliveryID}) async { + try { + state = GetSuratJalanDetailStateLoading(); + final dio = ref.read(dioProvider); + final resp = await SuratJalanRepository(dio: dio) + .getSuratJalanSetail(token: token, deliveryID: deliveryID); + state = GetSuratJalanDetailStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = GetSuratJalanDetailStateError(message: e.message.toString()); + } else { + state = GetSuratJalanDetailStateError(message: e.toString()); + } + } + } +} + +//provider +final GetSuratJalanDetailProvider = StateNotifierProvider< + GetSuratJalanDetailNotifier, + GetSuratJalanDetailState>((ref) => GetSuratJalanDetailNotifier(ref: ref)); diff --git a/lib/screen/surat_jalan/get_surat_jalan_list_provider.dart b/lib/screen/surat_jalan/get_surat_jalan_list_provider.dart new file mode 100644 index 0000000..c275344 --- /dev/null +++ b/lib/screen/surat_jalan/get_surat_jalan_list_provider.dart @@ -0,0 +1,81 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/model/surat_jalan_model.dart'; +import 'package:mitra_corporate/repository/surat_jalan_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class GetSuratJalanListState extends Equatable { + final DateTime date; + const GetSuratJalanListState(this.date); + @override + List get props => [date]; +} + +class GetSuratJalanListStateInit extends GetSuratJalanListState { + GetSuratJalanListStateInit() : super(DateTime.now()); +} + +class GetSuratJalanListStateLoading extends GetSuratJalanListState { + GetSuratJalanListStateLoading() : super(DateTime.now()); +} + +class GetSuratJalanListStateError extends GetSuratJalanListState { + final String message; + GetSuratJalanListStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class GetSuratJalanListStateDone extends GetSuratJalanListState { + final SearchSuratJalanModel model; + GetSuratJalanListStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class GetSuratJalanListNotifier extends StateNotifier { + final Ref ref; + GetSuratJalanListNotifier({ + required this.ref, + }) : super(GetSuratJalanListStateInit()); + + void getData({ + required String token, + required String company_id, + required String rpp, + required String keyword, + required String page, + required String sd, + required String ed, + required String dateType, + }) async { + try { + state = GetSuratJalanListStateLoading(); + final dio = ref.read(dioProvider); + final resp = await SuratJalanRepository(dio: dio).getSuratJalan( + token: token, + company_id: company_id, + endDate: ed, + startDate: sd, + rpp: rpp, + keyword: keyword, + dateType: dateType, + page: page); + state = GetSuratJalanListStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = GetSuratJalanListStateError(message: e.message.toString()); + } else { + state = GetSuratJalanListStateError(message: e.toString()); + } + } + } +} + +//provider +final GetSuratJalanListProvider = + StateNotifierProvider( + (ref) => GetSuratJalanListNotifier(ref: ref)); diff --git a/lib/screen/surat_jalan/list_pasien_terpilih.dart b/lib/screen/surat_jalan/list_pasien_terpilih.dart new file mode 100644 index 0000000..e8c697e --- /dev/null +++ b/lib/screen/surat_jalan/list_pasien_terpilih.dart @@ -0,0 +1,247 @@ +import 'dart:ui'; + +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/model/order_model.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; + +class ListPasienTerpilih extends HookConsumerWidget { + const ListPasienTerpilih( + {super.key, + required this.orders, + required this.loading, + required this.unselect}); + final List orders; + final bool loading; + final Function unselect; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final scrollCtr = useScrollController(); + return Material( + child: Container( + width: Constant.getActualX(context: context, x: 585), + height: Constant.getActualY(context: context, y: 410), + color: Colors.white, + child: Column( + children: [ + Row( + children: [ + Container( + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 12)), + alignment: Alignment.centerLeft, + width: Constant.getActualX(context: context, x: 180), + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Text('No Registrasi / Nama', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + Container( + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 12)), + alignment: Alignment.centerLeft, + width: Constant.getActualX(context: context, x: 325), + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Text('Pemeriksaan', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + // Container( + // padding: EdgeInsets.only( + // left: Constant.getActualX(context: context, x: 12)), + // alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 200), + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + // child: Text('Jenis Specimen', + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textGrey)), + // ), + Container( + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 12)), + alignment: Alignment.centerLeft, + width: Constant.getActualX(context: context, x: 80), + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Text('Aksi', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 340), + // color: Colors.red, + child: loading + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 40), + ) + : Scrollbar( + thickness: 5, + interactive: true, + thumbVisibility: true, + trackVisibility: true, + scrollbarOrientation: ScrollbarOrientation.right, + controller: scrollCtr, + child: ScrollConfiguration( + behavior: ScrollConfiguration.of(context) + .copyWith(dragDevices: { + PointerDeviceKind.mouse, + PointerDeviceKind.touch, + PointerDeviceKind.trackpad, + PointerDeviceKind.stylus, + PointerDeviceKind.unknown + }), + child: ListView( + controller: scrollCtr, + scrollDirection: Axis.vertical, + children: orders + .asMap() + .entries + .map( + (e) => Container( + padding: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 10), + bottom: Constant.getActualY( + context: context, y: 10)), + color: e.key.isOdd + ? Constant.primaryBlue + .withOpacity(0.05) + : Colors.white, + margin: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 5), + bottom: Constant.getActualY( + context: context, y: 5)), + child: Row( + children: [ + Container( + padding: EdgeInsets.only( + left: Constant.getActualX( + context: context, x: 12)), + alignment: Alignment.centerLeft, + width: Constant.getActualX( + context: context, x: 180), + height: Constant.getActualY( + context: context, y: 64), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text(e.value.orderNumber ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: Constant + .textBlack)), + Text(e.value.patientName ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: Constant + .textBlack)) + ], + ), + ), + // Container( + // padding: EdgeInsets.only( + // left: Constant.getActualX( + // context: context, x: 12)), + // alignment: Alignment.centerLeft, + // width: Constant.getActualX( + // context: context, x: 195), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Colors.white, + // child: Text(e.patientName ?? "", + // style: Constant.body3_600( + // context: context) + // .copyWith( + // color: Constant + // .textBlack)), + // ), + SizedBox( + width: Constant.getActualX( + context: context, x: 325), + child: Wrap( + alignment: WrapAlignment.start, + spacing: 10, + children: [ + if (e.value.tests!.isNotEmpty) + ...e.value.tests! + .map((k) => Container( + margin: EdgeInsets.only( + bottom: Constant + .getActualX( + context: + context, + x: 10)), + child: Chip( + shape: + RoundedRectangleBorder( + borderRadius: BorderRadius + .all(Radius + .circular( + 6)), + ), + // backgroundColor: + // Constant + // .blue_016, + label: Text( + '$k '.replaceAll( + ' ', + '\u00A0'), + overflow: + TextOverflow + .ellipsis, + style: Constant.caption2_600( + context: + context) + .copyWith( + color: + Constant.textBlack)), + ), + )) + ], + ), + ), + Container( + alignment: Alignment.centerLeft, + width: Constant.getActualX( + context: context, x: 80), + child: IconButton( + onPressed: () { + // print(e.orderNumber); + unselect(e.value); + }, + icon: Icon( + EvaIcons.arrowCircleLeft, + size: 24, + color: + Constant.primaryRed)), + ) + ], + ), + ), + ) + .toList(), + ), + ), + ), + ), + ], + ) + // + )); + } +} diff --git a/lib/screen/surat_jalan/send_qr_code_provider.dart b/lib/screen/surat_jalan/send_qr_code_provider.dart new file mode 100644 index 0000000..7784216 --- /dev/null +++ b/lib/screen/surat_jalan/send_qr_code_provider.dart @@ -0,0 +1,65 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mitra_corporate/repository/surat_jalan_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class SendQrCodeState extends Equatable { + final DateTime date; + const SendQrCodeState(this.date); + @override + List get props => [date]; +} + +class SendQrCodeStateInit extends SendQrCodeState { + SendQrCodeStateInit() : super(DateTime.now()); +} + +class SendQrCodeStateLoading extends SendQrCodeState { + SendQrCodeStateLoading() : super(DateTime.now()); +} + +class SendQrCodeStateError extends SendQrCodeState { + final String message; + SendQrCodeStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class SendQrCodeStateDone extends SendQrCodeState { + final String model; + SendQrCodeStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class SendQrCodeNotifier extends StateNotifier { + final Ref ref; + SendQrCodeNotifier({ + required this.ref, + }) : super(SendQrCodeStateInit()); + + void senQrCode( + {required String token, required List arrOrderID}) async { + try { + state = SendQrCodeStateLoading(); + final dio = ref.read(dioProvider); + final resp = await SuratJalanRepository(dio: dio) + .sendQrCode(token: token, arrOrderID: arrOrderID); + state = SendQrCodeStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = SendQrCodeStateError(message: e.message.toString()); + } else { + state = SendQrCodeStateError(message: e.toString()); + } + } + } +} + +//provider +final SendQrCodeProvider = + StateNotifierProvider( + (ref) => SendQrCodeNotifier(ref: ref)); diff --git a/lib/screen/surat_jalan/send_surat_jalan_provider.dart b/lib/screen/surat_jalan/send_surat_jalan_provider.dart new file mode 100644 index 0000000..6c072ab --- /dev/null +++ b/lib/screen/surat_jalan/send_surat_jalan_provider.dart @@ -0,0 +1,65 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'package:mitra_corporate/repository/surat_jalan_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class SendSuratJalanState extends Equatable { + final DateTime date; + const SendSuratJalanState(this.date); + @override + List get props => [date]; +} + +class SendSuratJalanStateInit extends SendSuratJalanState { + SendSuratJalanStateInit() : super(DateTime.now()); +} + +class SendSuratJalanStateLoading extends SendSuratJalanState { + SendSuratJalanStateLoading() : super(DateTime.now()); +} + +class SendSuratJalanStateError extends SendSuratJalanState { + final String message; + SendSuratJalanStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class SendSuratJalanStateDone extends SendSuratJalanState { + final String number; + SendSuratJalanStateDone({ + required this.number, + }) : super(DateTime.now()); +} + +//notifier +class SendSuratJalanNotifier extends StateNotifier { + final Ref ref; + SendSuratJalanNotifier({ + required this.ref, + }) : super(SendSuratJalanStateInit()); + + void Send({required String token, required String deliveryID}) async { + try { + state = SendSuratJalanStateLoading(); + final dio = ref.read(dioProvider); + final resp = await SuratJalanRepository(dio: dio) + .sendSuratJalan(token: token, deliveryID: deliveryID); + state = SendSuratJalanStateDone(number: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = SendSuratJalanStateError(message: e.message.toString()); + } else { + state = SendSuratJalanStateError(message: e.toString()); + } + } + } +} + +//provider +final SendSuratJalanProvider = + StateNotifierProvider( + (ref) => SendSuratJalanNotifier(ref: ref)); diff --git a/lib/screen/surat_jalan/surat_jalan_confirmation.dart b/lib/screen/surat_jalan/surat_jalan_confirmation.dart new file mode 100644 index 0000000..2974774 --- /dev/null +++ b/lib/screen/surat_jalan/surat_jalan_confirmation.dart @@ -0,0 +1,421 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/date_symbol_data_local.dart'; +import 'package:intl/intl.dart'; +import 'package:mitra_corporate/app/constant.dart'; +import 'package:mitra_corporate/model/create_surat_jalan_model.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/provider/surat_jalan_provider.dart'; +import 'package:mitra_corporate/screen/surat_jalan/create_surat_jalan_provider.dart'; +import 'package:mitra_corporate/screen/surat_jalan/dialog_send_qr.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../widgets/custom_snackbar_widget.dart'; + +class SuratJalanConfirmation extends HookConsumerWidget { + const SuratJalanConfirmation({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final tempSuratjalan = ref.watch(createSuratJalanProvider); + final auth = ref.watch(authProvider); + final date = useState(""); + final loading = useState(false); + final number = useState(""); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + DateTime now = DateTime.parse(tempSuratjalan.order!.date!); + // DateTime now = DateTime.now(); + initializeDateFormatting('id', '').then((_) { + date.value = DateFormat('EEEE, d MMMM yyyy', "id").format(now); + print(DateFormat('EEEE, d MMMM yyyy', "id").format(now)); + }); + }); + return () {}; + }, []); + + addOrder() { + // print(jsonEncode(tempSuratjalan)); + // return; + + ref.read(CreateSuratJalanProvider.notifier).addOrder(prm: tempSuratjalan); + } + + ref.listen( + CreateSuratJalanProvider, + (previous, next) { + if (next is CreateSuratJalanStateInit) { + loading.value = true; + } else if (next is CreateSuratJalanStateLoading) { + loading.value = true; + } else if (next is CreateSuratJalanStateError) { + SanckbarWidget(context, next.message, snackbarType.error); + loading.value = false; + Constant.autoLogout(context: context, msg: next.message); + } else if (next is CreateSuratJalanStateDone) { + // print(jsonEncode(next.model)); + number.value = next.number; + SanckbarWidget( + context, + "Pengiriman order berhasil di buat ${next.number}", + snackbarType.success); + List arrOrderID = List.empty(growable: true); + for (var element in tempSuratjalan.orderDetail!) { + arrOrderID.add(element.orderId!); + } + Navigator.pop(context); + ref.read(createSuratJalanProvider.notifier).state = + CreateSuratJalanModel(); + ref.read(suratJalanProvider.state).update((state) => 1); + + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) => DialogSendQr( + arrOrderID: arrOrderID, + orderNumber: next.number, + ), + ); + + loading.value = false; + } + }, + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Anda yakin dengan pengiriman order berikut ?", + style: Constant.h4_600(context: context), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 28), + ), + Row( + children: [ + Expanded( + flex: 3, + child: Container( + margin: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 24)), + child: Text( + "Regional", + style: Constant.body1_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + Expanded( + flex: 9, + child: Text( + tempSuratjalan.order?.regionalName ?? "", + style: Constant.body1_600(context: context), + )) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + Row( + children: [ + Expanded( + flex: 3, + child: Container( + margin: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 24)), + child: Text( + "Cabang", + style: Constant.body1_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + Expanded( + flex: 9, + child: Text( + tempSuratjalan.order?.destinationName ?? "", + style: Constant.body1_600(context: context), + )) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + Row( + children: [ + Expanded( + flex: 3, + child: Container( + margin: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 24)), + child: Text( + "Staff PIC", + style: Constant.body1_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + Expanded( + flex: 9, + child: Text( + auth?.mUserUsername ?? "", + style: Constant.body1_600(context: context), + )) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + Row( + children: [ + Expanded( + flex: 3, + child: Container( + margin: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 24)), + child: Text( + "Nomor Referensi", + style: Constant.body1_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + Expanded( + flex: 9, + child: Text( + tempSuratjalan.order?.noRef ?? "", + style: Constant.body1_600(context: context), + )) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + Row( + children: [ + Expanded( + flex: 3, + child: Container( + margin: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 24)), + child: Text( + "Tanggal Kirim Order", + style: Constant.body1_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + Expanded( + flex: 9, + child: Text( + date.value, + style: Constant.body1_600(context: context), + )) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 20), + ), + Row( + children: [ + Expanded( + flex: 3, + child: Container( + margin: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 24)), + child: Text( + "Catatan", + style: Constant.body1_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + Expanded( + flex: 9, + child: Text( + tempSuratjalan.order?.note ?? "", + style: Constant.body1_600(context: context), + )) + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 28), + ), + SizedBox( + height: Constant.getActualY(context: context, y: 64), + child: Text( + "List Pasien Terpilih", + style: Constant.h4_600(context: context), + ), + ), + Container( + height: Constant.getActualY(context: context, y: 64), + decoration: BoxDecoration( + color: Constant.grey_200, borderRadius: BorderRadius.circular(8)), + child: Row( + children: [ + Expanded( + flex: 3, + child: Container( + margin: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 24)), + child: Text( + "Nomor Registrasi", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + Expanded( + flex: 3, + child: Container( + margin: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 24)), + child: Text( + "Nama Pasien", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + Expanded( + flex: 6, + child: Container( + margin: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 24)), + child: Text( + "Pemeriksaan", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey), + ), + )), + ], + ), + ), + ...tempSuratjalan.orderDetail!.map((e) => Container( + constraints: BoxConstraints( + minHeight: Constant.getActualX(context: context, x: 64)), + margin: EdgeInsets.only( + bottom: Constant.getActualX(context: context, x: 10)), + child: Row( + children: [ + Expanded( + flex: 3, + child: Container( + margin: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 24)), + child: Text( + e.orderNumber ?? "", + style: Constant.body3_600(context: context), + ), + )), + Expanded( + flex: 3, + child: Container( + margin: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 24)), + child: Text( + e.patientName ?? "", + style: Constant.body3_600(context: context), + ), + )), + Expanded( + flex: 6, + child: Container( + margin: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 24)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + children: [ + ...e.tests!.map((i) => Container( + margin: EdgeInsets.only( + top: Constant.getActualX( + context: context, x: 10), + right: Constant.getActualX( + context: context, x: 10), + bottom: Constant.getActualX( + context: context, x: 10)), + child: Chip( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(6)), + ), + // backgroundColor: + // Constant.grey_200, + label: Text( + '$i '.replaceAll(' ', '\u00A0'), + overflow: TextOverflow.ellipsis, + style: Constant.caption1_600( + context: context) + .copyWith( + color: Constant.textBlack)), + ), + )), + ], + ), + ], + ), + )), + ], + ), + )), + SizedBox( + height: Constant.getActualY(context: context, y: 12), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( + height: Constant.getActualY(context: context, y: 45), + child: OutlinedButton( + style: OutlinedButton.styleFrom( + backgroundColor: Colors.white, + side: BorderSide(color: Colors.grey, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + onPressed: !loading.value + ? () { + // ref.read(createSuratJalanProvider.notifier).state = + // CreateSuratJalanModel(); + ref + .read(suratJalanProvider.state) + .update((state) => 1); + + // Navigator.pop(context); + } + : null, + child: loading.value + ? LoadingAnimationWidget.staggeredDotsWave( + color: Constant.green, size: 20) + : Text('Batal', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textBlack))), + ), + SizedBox(width: Constant.getActualX(context: context, x: 24)), + Container( + height: Constant.getActualY(context: context, y: 45), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: Constant.green), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Constant.green, + side: BorderSide(color: Constant.green, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + onPressed: !loading.value + ? () { + addOrder(); + } + : null, + child: loading.value + ? LoadingAnimationWidget.staggeredDotsWave( + color: Colors.white, size: 20) + : Text('Simpan', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textWhite))), + ), + ], + ) + ], + ); + } +} diff --git a/lib/screen/surat_jalan/surat_jalan_detail_screen.dart b/lib/screen/surat_jalan/surat_jalan_detail_screen.dart new file mode 100644 index 0000000..e6f6cfc --- /dev/null +++ b/lib/screen/surat_jalan/surat_jalan_detail_screen.dart @@ -0,0 +1,83 @@ +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/provider/menu_provider.dart'; +import 'package:mitra_corporate/screen/surat_jalan/table_detail_surat_jalan.dart'; + +import 'package:mitra_corporate/widgets/header.dart'; + +import '../../app/constant.dart'; +import '../../provider/surat_jalan_provider.dart'; + +class SuratJalanDetailScreen extends HookConsumerWidget { + const SuratJalanDetailScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final sjNumber = ref.watch(suratJalanNumber); + + return Material( + child: Column( + children: [ + Header(), + Container( + child: Padding( + padding: const EdgeInsets.all(32.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + IconButton( + onPressed: () { + ref.read(currentMenuProvider.notifier).state = 3; + }, + icon: Icon(EvaIcons.arrowBackOutline)), + Text('Detail Surat Jalan', + style: Constant.h4_600(context: context) + .copyWith(color: Constant.textBlack)), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 40), + ), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: Colors.white, + ), + padding: EdgeInsets.symmetric( + horizontal: + Constant.getActualY(context: context, y: 24), + vertical: Constant.getActualY(context: context, y: 24)), + child: Column( + children: [ + Row(children: [ + Text('Nomor Surat Jalan', + style: Constant.body1_600(context: context) + .copyWith(color: Constant.textBlack)), + SizedBox( + width: + Constant.getActualX(context: context, x: 8)), + Text(sjNumber, + style: Constant.body1_600(context: context) + .copyWith(color: Constant.primaryRed)), + ]), + SizedBox( + height: + Constant.getActualY(context: context, y: 24)), + // SizedBox( + // height: Constant.getActualY(context: context, y: 24)), + TableDetailSuratJalan() + ], + ), + ), + ], + ), + ), + ) + ], + ), + ); + } +} diff --git a/lib/screen/surat_jalan/surat_jalan_screen.dart b/lib/screen/surat_jalan/surat_jalan_screen.dart new file mode 100644 index 0000000..0ced4b6 --- /dev/null +++ b/lib/screen/surat_jalan/surat_jalan_screen.dart @@ -0,0 +1,280 @@ +import 'package:mitra_corporate/model/surat_jalan_model.dart'; +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/model/create_surat_jalan_model.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/provider/surat_jalan_provider.dart'; + +import 'package:mitra_corporate/screen/surat_jalan/dialog_surat_jalan.dart'; +import 'package:mitra_corporate/screen/surat_jalan/table_surat_jalan.dart'; +import 'package:mitra_corporate/widgets/header.dart'; +import 'package:intl/intl.dart'; + +import '../../app/constant.dart'; +import '../../provider/menu_provider.dart'; +import '../../widgets/custom_text_field.dart'; + +class SuratJalanScreen extends HookConsumerWidget { + const SuratJalanScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final auth = ref.watch(authProvider); + final selectedDateType = ref.watch(sjDateType); + final isExpand = ref.watch(sideBarExpandProvider); + final searchCtr = useTextEditingController(text: ""); + final startDateCtr = useTextEditingController( + text: DateFormat('dd-MM-yyyy').format(ref.watch(sjStartDateProvider))); + final startDateState = useState(ref.watch(sjStartDateProvider)); + final endDateCtr = useTextEditingController( + text: DateFormat('dd-MM-yyyy').format(ref.watch(sjEndDateProvider))); + final endDateState = useState(ref.watch(sjEndDateProvider)); + + final dateFilterKey = useState(1); + + final selectedDateFilter = useState(selectedDateType); + useEffect(() { + // ref.read(sjDateType.notifier).state = dateFilter.first; + selectedDateFilter.value = selectedDateType; + dateFilterKey.value = dateFilterKey.value + 1; + return null; + }, [isExpand]); + // useEffect(() { + // selectedDateFilter.value = selectedDateType; + // print(selectedDateType.name); + // }, [selectedDateType]); + + return Material( + child: Column( + children: [ + Header(), + Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 32), + vertical: Constant.getActualY(context: context, y: 32)), + child: Column( + children: [ + Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 1079), + height: Constant.getActualY(context: context, y: 40), + child: Text('Kirim Order', + style: Constant.h3_400(context: context) + .copyWith(color: Constant.textBlack)), + ), + SizedBox(height: Constant.getActualY(context: context, y: 42)), + Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 24), + vertical: Constant.getActualY(context: context, y: 32)), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: Colors.white), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded(child: LayoutBuilder(builder: + (BuildContext context, + BoxConstraints constraints) { + return DropdownMenu( + key: ValueKey(dateFilterKey.value), + hintText: "Tipe Tanggal", + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderSide: BorderSide( + color: Constant.primaryRed), + borderRadius: BorderRadius.circular(8))), + menuHeight: + Constant.getActualY(context: context, y: 300), + // width: Constant.getActualX(context: context, x: 150), + width: constraints.maxWidth, + textStyle: Constant.body2_400(context: context), + label: const Text( + "Tipe Tanggal", + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + initialSelection: selectedDateFilter.value, + + onSelected: (dateFilterModel? value) { + // This is called when the user selects an item. + // print(value!.name); + ref.read(sjDateType.notifier).state = value!; + selectedDateFilter.value = value; + }, + dropdownMenuEntries: Constant.dateFilter + .map>( + (dateFilterModel value) { + return DropdownMenuEntry( + value: value, label: value.name); + }).toList(), + ); + })), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + Expanded( + child: TextField( + style: Constant.body1_600(context: context), + controller: startDateCtr, + decoration: InputDecoration( + suffixIcon: Icon(EvaIcons.calendar), + border: OutlineInputBorder( + borderSide: + BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + ), + labelStyle: + Constant.body1_600(context: context), + labelText: "Tanggal Awal" //label text of field + ), + readOnly: true, + onTap: () async { + DateTime? pickedDate = await showDatePicker( + // locale: Locale("id"), + confirmText: "OK", + cancelText: "Batal", + context: context, + initialDate: startDateState.value, + firstDate: DateTime(1800), + initialEntryMode: + DatePickerEntryMode.calendarOnly, + + //DateTime.now() - not to allow to choose before today. + lastDate: DateTime(2100)); + + if (pickedDate != null) { + print( + pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000 + String formattedDate = + DateFormat('dd-MM-yyyy').format(pickedDate); + print( + formattedDate); //formatted date output using intl package => 2021-03-16 + startDateCtr.text = + formattedDate; //set output date to TextField value. + startDateState.value = pickedDate; + ref.read(sjStartDateProvider.notifier).state = + pickedDate; + } else {} + }, + )), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + Expanded( + child: TextField( + style: Constant.body1_600(context: context), + controller: endDateCtr, + decoration: InputDecoration( + suffixIcon: Icon(EvaIcons.calendar), + border: OutlineInputBorder( + borderSide: + BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + ), + labelStyle: + Constant.body1_600(context: context), + labelText: "Tanggal Akhir" //label text of field + ), + readOnly: true, + onTap: () async { + DateTime? pickedDate = await showDatePicker( + // locale: Locale("id"), + confirmText: "OK", + cancelText: "Batal", + context: context, + initialDate: endDateState.value, + firstDate: DateTime(1800), + initialEntryMode: + DatePickerEntryMode.calendarOnly, + + //DateTime.now() - not to allow to choose before today. + lastDate: DateTime(2100)); + + if (pickedDate != null) { + print( + pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000 + String formattedDate = + DateFormat('dd-MM-yyyy').format(pickedDate); + print( + formattedDate); //formatted date output using intl package => 2021-03-16 + endDateCtr.text = + formattedDate; //set output date to TextField value. + endDateState.value = pickedDate; + ref.read(sjEndDateProvider.notifier).state = + pickedDate; + } else {} + }, + )), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + Expanded( + // width: Constant.getActualX(context: context, x: 408), + // height: Constant.getActualY(context: context, y: 56), + child: CustomTextField( + onChange: (value) { + ref + .read(suratJalankeywordProvider.notifier) + .state = value; + }, + controller: searchCtr, + isPassword: false, + hintText: "Cari No/Tujuan/Staff PIC", + labelText: "Cari No/Tujuan/Staff PIC", + suffixIcon: Icon(EvaIcons.search), + ), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + Container( + // width: Constant.getActualX(context: context, x: 171), + height: + Constant.getActualY(context: context, y: 56), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: Constant.primaryRed), + child: ElevatedButton.icon( + label: Text('Kirim order Baru', + style: Constant.button_large(context: context) + .copyWith(color: Constant.textWhite)), + icon: Icon(EvaIcons.plus, + color: Colors.white, size: 20.0), + onPressed: () { + ref + .read(createSuratJalanProvider.notifier) + .state = + CreateSuratJalanModel( + order: Order( + regionalId: + auth?.MUserSRegionalID ?? "")); + showDialog( + context: context, + barrierDismissible: false, + builder: ((context) => DialogSuratJalan())); + }, + style: ElevatedButton.styleFrom( + shadowColor: Colors.transparent), + ), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 24)), + TableSuratJalan() + ], + ), + ), + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/screen/surat_jalan/table_detail_surat_jalan.dart b/lib/screen/surat_jalan/table_detail_surat_jalan.dart new file mode 100644 index 0000000..cab6ae4 --- /dev/null +++ b/lib/screen/surat_jalan/table_detail_surat_jalan.dart @@ -0,0 +1,587 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/model/surat_jalan_detail_model.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/screen/surat_jalan/get_surat_jalan_detail_provider.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../provider/surat_jalan_provider.dart'; +import '../../widgets/custom_snackbar_widget.dart'; + +class TableDetailSuratJalan extends HookConsumerWidget { + const TableDetailSuratJalan({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final id = ref.watch(suratJalanID); + final auth = ref.watch(authProvider); + final loading = useState(false); + final data = useState>(List.empty()); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + ref + .read(GetSuratJalanDetailProvider.notifier) + .getData(token: auth?.token ?? "", deliveryID: id); + }); + return () {}; + }, [id]); + ref.listen( + GetSuratJalanDetailProvider, + (pref, next) { + if (next is GetSuratJalanDetailStateInit) { + loading.value = true; + } else if (next is GetSuratJalanDetailStateLoading) { + loading.value = true; + } else if (next is GetSuratJalanDetailStateError) { + loading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is GetSuratJalanDetailStateDone) { + // print(jsonEncode(next.model)); + data.value = next.model; + + loading.value = false; + } + }, + ); + return Material( + child: Column( + children: [ + Container( + height: Constant.getActualY(context: context, y: 64), + decoration: BoxDecoration( + color: Constant.grey_200, + border: Border(bottom: BorderSide(color: Colors.grey))), + child: Row( + children: [ + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 100), + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 20, right: 20), + child: Text('Tanggal', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 115), + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 20, right: 20), + child: Text('No Lab', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 196), + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 20, right: 20), + child: Text('Nama Pasien', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + // Expanded( + // flex: 1, + // child: Container( + // alignment: Alignment.centerLeft, + // // width: Constant.getActualX(context: context, x: 100), + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + // child: Padding( + // padding: const EdgeInsets.only( + // top: 12, bottom: 12, left: 20, right: 20), + // child: Text('Dikirim', + // overflow: TextOverflow.ellipsis, + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textGrey)), + // ), + // ), + // ), + // Expanded( + // flex: 1, + // child: Container( + // alignment: Alignment.centerLeft, + // // width: Constant.getActualX(context: context, x: 100), + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + // child: Padding( + // padding: const EdgeInsets.only( + // top: 12, bottom: 12, left: 20, right: 20), + // child: Text('Diterima', + // overflow: TextOverflow.ellipsis, + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textGrey)), + // ), + // ), + // ), + // Expanded( + // flex: 1, + // child: Container( + // alignment: Alignment.centerLeft, + // // width: Constant.getActualX(context: context, x: 100), + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + // child: Padding( + // padding: const EdgeInsets.only( + // top: 12, bottom: 12, left: 20, right: 20), + // child: Text('Ditolak', + // overflow: TextOverflow.ellipsis, + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textGrey)), + // ), + // ), + // ), + // Expanded( + // flex: 1, + // child: Container( + // alignment: Alignment.centerLeft, + // // width: Constant.getActualX(context: context, x: 100), + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + // child: Padding( + // padding: const EdgeInsets.only( + // top: 12, bottom: 12, left: 20, right: 20), + // child: Text('Proses', + // overflow: TextOverflow.ellipsis, + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textGrey)), + // ), + // ), + // ), + // Expanded( + // flex: 1, + // child: Container( + // alignment: Alignment.centerLeft, + // // width: Constant.getActualX(context: context, x: 100), + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + // child: Padding( + // padding: const EdgeInsets.only( + // top: 12, bottom: 12, left: 20, right: 20), + // child: Text('Selesai', + // overflow: TextOverflow.ellipsis, + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textGrey)), + // ), + // ), + // ), + // Expanded( + // flex: 1, + // child: Container( + // alignment: Alignment.centerLeft, + // // width: Constant.getActualX(context: context, x: 80), + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + // child: Padding( + // padding: const EdgeInsets.only( + // top: 12, bottom: 12, left: 20, right: 20), + // child: Text('Aksi', + // overflow: TextOverflow.ellipsis, + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textGrey)), + // ), + // ), + // ), + ], + ), + ), + Container( + height: Constant.getActualY(context: context, y: 580), + color: Colors.white, + child: loading.value + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 40), + ) + : ListView( + children: data.value + .map( + (e) => Row( + children: [ + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 100), + height: Constant.getActualY( + context: context, y: 64), + + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: Text(e.date ?? "", + style: + Constant.body3_600(context: context) + .copyWith( + color: Constant.textBlack)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + width: Constant.getActualX( + context: context, x: 115), + height: Constant.getActualY( + context: context, y: 64), + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: Text(e.orderNumber ?? "", + style: + Constant.body3_600(context: context) + .copyWith( + color: Constant.textBlack)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + width: Constant.getActualX( + context: context, x: 196), + height: Constant.getActualY( + context: context, y: 64), + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 20, + right: 20), + child: Text(e.patientName ?? "", + style: + Constant.body3_600(context: context) + .copyWith( + color: Constant.textBlack)), + ), + ), + ), + // Expanded( + // flex: 1, + // child: Container( + // alignment: Alignment.centerLeft, + // width: Constant.getActualX( + // context: context, x: 100), + // height: Constant.getActualY( + // context: context, y: 64), + // child: Padding( + // padding: const EdgeInsets.only( + // top: 12, + // bottom: 12, + // left: 20, + // right: 20), + // child: e.status == "S" || + // e.status == "R" || + // e.status == "T" || + // e.status == "X" || + // e.status == "P" || + // e.status == "Q" || + // e.status == "D" + // ? Icon( + // EvaIcons.checkmarkCircle2, + // size: 24, + // color: Constant.green, + // ) + // : null), + // ), + // ), + // Expanded( + // flex: 1, + // child: Container( + // alignment: Alignment.centerLeft, + // width: Constant.getActualX( + // context: context, x: 100), + // height: Constant.getActualY( + // context: context, y: 64), + // child: Padding( + // padding: const EdgeInsets.only( + // top: 12, + // bottom: 12, + // left: 20, + // right: 20), + // child: e.status == "R" || + // e.status == "T" || + // e.status == "P" || + // e.status == "Q" || + // e.status == "D" + // ? JustTheTooltip( + // preferredDirection: + // AxisDirection.up, + // backgroundColor: + // Constant.textGrey, + // elevation: 10, + // content: Padding( + // padding: EdgeInsets.all(10), + // child: + // Builder(builder: (context) { + // List> + // accepted = List.empty( + // growable: true); + // if (e.acceptedSample != + // null) { + // var splt = e.acceptedSample + // ?.split('|'); + // if (splt!.length > 0) { + // splt.forEach((element) { + // var splitted = + // element.split('@'); + // accepted.add({ + // "name": splitted[0], + // "type": splitted[1] + // }); + // }); + // } + // } + // return Wrap( + // spacing: 10, + // runSpacing: 10, + // children: accepted + // .map((e) => Chip( + // backgroundColor: e[ + // 'type'] == + // "S" + // ? Constant + // .yellow_016 + // : Constant + // .blue_016, + // label: Text( + // e['name'] ?? "", + // style: Constant + // .caption2_600( + // context: + // context) + // .copyWith( + // color: e['type'] == + // "S" + // ? Constant + // .textYellow + // : Constant + // .textBlue), + // ))) + // .toList(), + // ); + // }), + // ), + // child: Icon( + // EvaIcons.checkmarkCircle2, + // size: 24, + // color: Constant.green, + // ), + // ) + // : null), + // ), + // ), + // Expanded( + // flex: 1, + // child: Container( + // alignment: Alignment.centerLeft, + // width: Constant.getActualX( + // context: context, x: 100), + // height: Constant.getActualY( + // context: context, y: 64), + // child: Padding( + // padding: const EdgeInsets.only( + // top: 12, + // bottom: 12, + // left: 20, + // right: 20), + // child: e.status == "T" || e.status == "X" + // ? JustTheTooltip( + // preferredDirection: + // AxisDirection.up, + // backgroundColor: Constant.textGrey, + // elevation: 10, + // content: Padding( + // padding: EdgeInsets.all(10), + // child: + // Builder(builder: (context) { + // List> + // rejected = + // List.empty(growable: true); + // if (e.rejectedSample != null) { + // var splt = e.rejectedSample + // ?.split('|'); + // if (splt!.length > 0) { + // splt.forEach((element) { + // var splitted = + // element.split('@'); + // rejected.add({ + // "name": splitted[0], + // "type": splitted[1] + // }); + // }); + // } + // } + // return Wrap( + // spacing: 10, + // runSpacing: 10, + // children: rejected + // .map((e) => Chip( + // backgroundColor: e[ + // 'type'] == + // "S" + // ? Constant + // .yellow_016 + // : Constant + // .blue_016, + // label: Text( + // e['name'] ?? "", + // style: Constant + // .caption2_600( + // context: + // context) + // .copyWith( + // color: e[ + // 'type'] == + // "S" + // ? Constant + // .textYellow + // : Constant + // .textBlue), + // ))) + // .toList(), + // ); + // }), + // ), + // child: Icon( + // EvaIcons.checkmarkCircle2, + // size: 24, + // color: Constant.green, + // ), + // ) + // : null, + // ), + // ), + // ), + // Expanded( + // flex: 1, + // child: Container( + // alignment: Alignment.centerLeft, + // width: Constant.getActualX( + // context: context, x: 100), + // height: Constant.getActualY( + // context: context, y: 64), + // child: Padding( + // padding: const EdgeInsets.only( + // top: 12, + // bottom: 12, + // left: 20, + // right: 20), + // child: e.status == "Q" || + // e.status == "P" || + // e.status == "D" + // ? Icon( + // EvaIcons.checkmarkCircle2, + // size: 24, + // color: Constant.green, + // ) + // : null), + // ), + // ), + // Expanded( + // flex: 1, + // child: Container( + // alignment: Alignment.centerLeft, + // width: Constant.getActualX( + // context: context, x: 100), + // height: Constant.getActualY( + // context: context, y: 64), + // child: Padding( + // padding: const EdgeInsets.only( + // top: 12, + // bottom: 12, + // left: 20, + // right: 20), + // child: e.status == "D" + // ? Icon( + // EvaIcons.checkmarkCircle2, + // size: 24, + // color: Constant.green, + // ) + // : null), + // ), + // ), + // Expanded( + // flex: 1, + // child: Container( + // alignment: Alignment.centerLeft, + // width: Constant.getActualX( + // context: context, x: 80), + // height: Constant.getActualY( + // context: context, y: 64), + // child: Padding( + // padding: const EdgeInsets.only( + // top: 12, + // bottom: 12, + // left: 20, + // right: 20), + // child: InkWell( + // onTap: e.status == "D" + // ? () { + // showDialog( + // context: context, + // // barrierDismissible: false, + // builder: ((context) => + // Container( + // child: DialogPrint( + // url: + // "http://devone.aplikasi.web.id/birt/run?__report=report/one/lab/rpt_fo_001.rptdesign&__format=pdf&username=ADMIN&PID=131868?t=1696927533769"), + // ))); + // } + // : null, + // child: Tooltip( + // message: "Cetak laporan", + // child: Icon( + // EvaIcons.printer, + // size: 24, + // color: e.status == "D" + // ? Constant.primaryBlue + // : Colors.grey, + // ), + // ), + // )), + // ), + // ), + ], + ), + ) + .toList(), + ), + ), + ], + ), + ); + } +} diff --git a/lib/screen/surat_jalan/table_list_pasien.dart b/lib/screen/surat_jalan/table_list_pasien.dart new file mode 100644 index 0000000..0d5d23a --- /dev/null +++ b/lib/screen/surat_jalan/table_list_pasien.dart @@ -0,0 +1,246 @@ +import 'dart:ui'; + +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/model/order_model.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; + +class TableListPasien extends HookConsumerWidget { + const TableListPasien( + {super.key, + required this.orders, + required this.loading, + required this.selected}); + final List orders; + final bool loading; + final Function selected; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final scrollCtr = useScrollController(); + return Material( + child: Container( + width: Constant.getActualX(context: context, x: 585), + height: Constant.getActualY(context: context, y: 410), + color: Colors.white, + child: Column( + children: [ + Row( + children: [ + Container( + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 12)), + alignment: Alignment.centerLeft, + width: Constant.getActualX(context: context, x: 180), + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Text('No Registrasi / Nama', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + Container( + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 12)), + alignment: Alignment.centerLeft, + width: Constant.getActualX(context: context, x: 325), + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Text('Pemeriksaan', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + // Container( + // padding: EdgeInsets.only( + // left: Constant.getActualX(context: context, x: 12)), + // alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 200), + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + // child: Text('Jenis Specimen', + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textGrey)), + // ), + Container( + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 12)), + alignment: Alignment.centerLeft, + width: Constant.getActualX(context: context, x: 80), + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Text('Aksi', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 340), + // color: Colors.red, + child: loading + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 40), + ) + : Scrollbar( + thickness: 5, + interactive: true, + thumbVisibility: true, + trackVisibility: true, + scrollbarOrientation: ScrollbarOrientation.right, + controller: scrollCtr, + child: ScrollConfiguration( + behavior: ScrollConfiguration.of(context) + .copyWith(dragDevices: { + PointerDeviceKind.mouse, + PointerDeviceKind.touch, + PointerDeviceKind.trackpad, + PointerDeviceKind.stylus, + PointerDeviceKind.unknown + }), + child: ListView( + controller: scrollCtr, + scrollDirection: Axis.vertical, + children: orders + .asMap() + .entries + .map( + (e) => Container( + padding: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 10), + bottom: Constant.getActualY( + context: context, y: 10)), + color: e.key.isOdd + ? Constant.primaryBlue + .withOpacity(0.05) + : Colors.white, + margin: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 5), + bottom: Constant.getActualY( + context: context, y: 5)), + child: Row( + children: [ + Container( + padding: EdgeInsets.only( + left: Constant.getActualX( + context: context, x: 12)), + alignment: Alignment.centerLeft, + width: Constant.getActualX( + context: context, x: 180), + height: Constant.getActualY( + context: context, y: 64), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text(e.value.orderNumber ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: Constant + .textBlack)), + Text(e.value.patientName ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: Constant + .textBlack)) + ], + ), + ), + // Container( + // padding: EdgeInsets.only( + // left: Constant.getActualX( + // context: context, x: 12)), + // alignment: Alignment.centerLeft, + // width: Constant.getActualX( + // context: context, x: 195), + // height: Constant.getActualY( + // context: context, y: 64), + // color: Colors.white, + // child: Text(e.patientName ?? "", + // style: Constant.body3_600( + // context: context) + // .copyWith( + // color: Constant.textBlack)), + // ), + SizedBox( + // color: Colors.red, + width: Constant.getActualX( + context: context, x: 325), + child: Wrap( + alignment: WrapAlignment.start, + spacing: 10, + children: [ + if (e.value.tests!.isNotEmpty) + ...e.value.tests! + .map((k) => Container( + margin: EdgeInsets.only( + bottom: Constant + .getActualX( + context: + context, + x: 10)), + child: Chip( + shape: + RoundedRectangleBorder( + borderRadius: BorderRadius + .all(Radius + .circular( + 6)), + ), + // backgroundColor: + // Constant + // .blue_016, + label: Text( + '$k '.replaceAll( + ' ', + '\u00A0'), + overflow: + TextOverflow + .ellipsis, + style: Constant.caption2_600( + context: + context) + .copyWith( + color: + Constant.textBlack)), + ), + )) + ], + ), + ), + Container( + alignment: Alignment.centerLeft, + width: Constant.getActualX( + context: context, x: 80), + child: IconButton( + onPressed: () { + // print(e.orderNumber); + selected(e.value); + }, + icon: Icon( + EvaIcons.arrowCircleRight, + size: 24, + color: Constant.green)), + ) + ], + ), + ), + ) + .toList(), + ), + ), + ), + ), + ], + ) + // + )); + } +} diff --git a/lib/screen/surat_jalan/table_surat_jalan.dart b/lib/screen/surat_jalan/table_surat_jalan.dart new file mode 100644 index 0000000..bc0840e --- /dev/null +++ b/lib/screen/surat_jalan/table_surat_jalan.dart @@ -0,0 +1,669 @@ +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/model/surat_jalan_model.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/provider/surat_jalan_provider.dart'; +import 'package:mitra_corporate/screen/surat_jalan/cancel_surat_jalan_provider.dart'; +import 'package:mitra_corporate/screen/surat_jalan/get_surat_jalan_list_provider.dart'; +import 'package:mitra_corporate/screen/surat_jalan/send_surat_jalan_provider.dart'; +import 'package:intl/intl.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../../app/constant.dart'; +import '../../provider/menu_provider.dart'; +import '../../widgets/custom_snackbar_widget.dart'; +import '../registrasi/table_pasien.dart'; +import 'dialog_cancel.dart'; +import 'dialog_send.dart'; + +class TableSuratJalan extends HookConsumerWidget { + const TableSuratJalan({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + // final isExpand = ref.watch(sideBarExpandProvider); + final keyword = ref.watch(suratJalankeywordProvider); + final startDate = ref.watch(sjStartDateProvider); + final endDate = ref.watch(sjEndDateProvider); + final sjdateType = ref.watch(sjDateType); + final auth = ref.watch(authProvider); + final suratJalanTab = ref.watch(suratJalanProvider); + final rowsPerPage = useState(10); + final searchLoading = useState(false); + final dataStart = useState(1); + final dataEnd = useState(1); + final currPage = useState(1); + final sjList = useState( + SearchSuratJalanModel(suratJalan: [], total: "0", totalPage: 0)); + final cancelLoading = useState(false); + final sendLoading = useState(false); + onRefresh() { + var ed = DateFormat('yyyy-MM-dd').format(endDate).toString(); + var sd = DateFormat('yyyy-MM-dd').format(startDate).toString(); + ref.read(GetSuratJalanListProvider.notifier).getData( + ed: ed, + sd: sd, + token: auth?.token ?? "", + page: currPage.value.toString(), + keyword: keyword, + rpp: rowsPerPage.value.toString(), + dateType: sjdateType.id, + company_id: auth?.mUserMCompanyID ?? ""); + } + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final debouncer = Debouncer(milliseconds: 100); + debouncer.run(() { + onRefresh(); + }); + }); + return () {}; + }, [currPage.value, suratJalanTab]); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final debouncer = Debouncer(milliseconds: 100); + currPage.value = 1; + debouncer.run(() { + var ed = DateFormat('yyyy-MM-dd').format(endDate).toString(); + var sd = DateFormat('yyyy-MM-dd').format(startDate).toString(); + ref.read(GetSuratJalanListProvider.notifier).getData( + ed: ed, + sd: sd, + token: auth?.token ?? "", + page: "1", + keyword: keyword, + dateType: sjdateType.id, + rpp: rowsPerPage.value.toString(), + company_id: auth?.mUserMCompanyID ?? ""); + }); + }); + return () {}; + }, [ + keyword, + startDate, + endDate, + sjdateType, + rowsPerPage.value, + ]); + + ref.listen( + GetSuratJalanListProvider, + (pref, next) { + if (next is GetSuratJalanListStateInit) { + searchLoading.value = true; + } else if (next is GetSuratJalanListStateLoading) { + searchLoading.value = true; + } else if (next is GetSuratJalanListStateError) { + searchLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is GetSuratJalanListStateDone) { + sjList.value = next.model; + // print(jsonEncode(next.model)); + + dataStart.value = + (currPage.value * rowsPerPage.value) - rowsPerPage.value + 1; + dataEnd.value = currPage.value == next.model.totalPage + ? int.parse(next.model.total ?? "0") + : dataStart.value + rowsPerPage.value - 1; + + searchLoading.value = false; + } + }, + ); + cancelSuratJalan(String deliveryID) { + ref + .read(CancelSuratJalanProvider.notifier) + .cancel(token: auth?.token ?? "", deliveryID: deliveryID); + } + + ref.listen( + CancelSuratJalanProvider, + (pref, next) { + if (next is CancelSuratJalanStateInit) { + cancelLoading.value = true; + } else if (next is CancelSuratJalanStateLoading) { + cancelLoading.value = true; + } else if (next is CancelSuratJalanStateError) { + cancelLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is CancelSuratJalanStateDone) { + var ed = DateFormat('yyyy-MM-dd').format(endDate).toString(); + var sd = DateFormat('yyyy-MM-dd').format(startDate).toString(); + ref.read(GetSuratJalanListProvider.notifier).getData( + dateType: sjdateType.id, + ed: ed, + sd: sd, + token: auth?.token ?? "", + page: currPage.value.toString(), + keyword: keyword, + rpp: rowsPerPage.value.toString(), + company_id: auth?.mUserMCompanyID ?? ""); + Navigator.pop(context); + // print(jsonEncode(next.model)); + SanckbarWidget(context, "Berhasil Membatalkan pengiriman order", + snackbarType.success); + + cancelLoading.value = false; + } + }, + ); + sendSuratJalan(String deliveryID) { + ref + .read(SendSuratJalanProvider.notifier) + .Send(token: auth?.token ?? "", deliveryID: deliveryID); + } + + ref.listen( + SendSuratJalanProvider, + (pref, next) { + if (next is SendSuratJalanStateInit) { + sendLoading.value = true; + } else if (next is SendSuratJalanStateLoading) { + sendLoading.value = true; + } else if (next is SendSuratJalanStateError) { + sendLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + Constant.autoLogout(context: context, msg: next.message); + } else if (next is SendSuratJalanStateDone) { + var ed = DateFormat('yyyy-MM-dd').format(endDate).toString(); + var sd = DateFormat('yyyy-MM-dd').format(startDate).toString(); + ref.read(GetSuratJalanListProvider.notifier).getData( + dateType: sjdateType.id, + ed: ed, + sd: sd, + token: auth?.token ?? "", + page: currPage.value.toString(), + keyword: keyword, + rpp: rowsPerPage.value.toString(), + company_id: auth?.mUserMCompanyID ?? ""); + Navigator.pop(context); + // print(jsonEncode(next.model)); + SanckbarWidget(context, "Berhasil", snackbarType.success); + + sendLoading.value = false; + } + }, + ); + return Material( + borderRadius: BorderRadius.all(Radius.circular(8)), + child: Column( + children: [ + Container( + // height: Constant.getActualY(context: context, y: 64), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8)), + color: Constant.grey_200, + ), + child: Row( + children: [ + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 115), + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 24, right: 6), + child: Text('Tanggal Surat Jalan', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 115), + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 6, right: 6), + child: Text('Tanggal Kedatangan', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 24, right: 24), + child: Text('Nomor Pengiriman', + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 337), + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 24, right: 24), + child: Text('Staf PIC', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 175), + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 24, right: 24), + child: Text('Tujuan', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 106), + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + child: Padding( + padding: const EdgeInsets.only( + top: 12, bottom: 12, left: 24, right: 24), + child: Text('Aksi', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ), + ), + ], + ), + ), + Container( + // width: Constant.getActualX(context: context, x: 1080), + color: Colors.white, + height: Constant.getActualY(context: context, y: 466), + child: searchLoading.value + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 40)) + : ListView( + children: sjList.value.suratJalan! + .map((e) => Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + flex: 2, + child: Container( + // width: Constant.getActualX(context: context, x: 115), + height: Constant.getActualY( + context: context, y: 64), + color: Colors.white, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 24, + right: 6), + child: Text(e.dateSj ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: + Constant.textBlack)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + // width: Constant.getActualX(context: context, x: 115), + height: Constant.getActualY( + context: context, y: 64), + color: Colors.white, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 6, + right: 6), + child: Text(e.date ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: + Constant.textBlack)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 171), + height: Constant.getActualY( + context: context, y: 64), + color: Colors.white, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 24, + right: 24), + child: Text(e.orderNumber ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: + Constant.textBlack)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 337), + height: Constant.getActualY( + context: context, y: 64), + color: Colors.white, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 24, + right: 24), + child: Text(e.pic ?? "", + style: Constant.body3_600( + context: context) + .copyWith( + color: + Constant.textBlack)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 175), + height: Constant.getActualY( + context: context, y: 64), + color: Colors.white, + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 24, + right: 24), + child: Text(e.destination ?? '', + style: Constant.body3_600( + context: context) + .copyWith( + color: + Constant.textBlack)), + ), + ), + ), + Expanded( + flex: 2, + child: Container( + alignment: Alignment.centerLeft, + // width: Constant.getActualX(context: context, x: 105), + height: Constant.getActualY( + context: context, y: 64), + child: Padding( + padding: const EdgeInsets.only( + top: 12, + bottom: 12, + left: 24, + right: 0), + child: Wrap( + children: [ + InkWell( + onTap: () { + ref + .read( + suratJalanID.notifier) + .state = e.id.toString(); + ref + .read(suratJalanNumber + .notifier) + .state = + e.orderNumber.toString(); + ref + .read(currentMenuProvider + .notifier) + .state = 5; + }, + child: Tooltip( + message: "Detail Pengiriman", + child: Icon(EvaIcons.maximize, + size: 24, + color: Constant.green), + ), + ), + SizedBox( + width: Constant.getActualX( + context: context, x: 10)), + // InkWell( + // onTap: () { + // // html.window.open( + // // 'http://devone.aplikasi.web.id/birt/run?__report=report/one/lab/rpt_fo_001.rptdesign&__format=pdf&username=ADMIN&PID=131868?t=1696927533769', + // // "_blank"); + // showDialog( + // context: context, + // // barrierDismissible: false, + // builder: ((context) => + // Container( + // child: DialogPrint( + // url: + // "http://devkedungdororaya.aplikasi.web.id/birt/run?__report=report/one/regional/rpt_mt_001.rptdesign&PID=${e.id}&username=${auth?.mUserUsername}&__format=pdf"), + // ))); + // }, + // child: Tooltip( + // message: "Cetak Surat Jalan ", + // child: Icon(EvaIcons.printer, + // size: 24, + // color: Constant.green), + // ), + // ), + // SizedBox( + // width: Constant.getActualX( + // context: context, x: 10)), + if (e.status == 'N') + InkWell( + onTap: () { + showDialog( + context: context, + barrierDismissible: + !sendLoading.value, + builder: (BuildContext + context) => + DialogConfirmSuratJalan( + sendFunction: + sendSuratJalan, + deliveryID: + e.id ?? "0", + loading: + cancelLoading + .value, + date: + e.date ?? "", + deliveryNumber: + e.orderNumber ?? + "", + pic: e.pic ?? ""), + ); + }, + child: Tooltip( + message: "Kirim", + child: Icon( + Icons.task_rounded, + size: 24, + color: Constant.green), + ), + ), + SizedBox( + width: Constant.getActualX( + context: context, x: 10)), + if (e.status == 'N') + InkWell( + onTap: () { + showDialog( + context: context, + barrierDismissible: + !cancelLoading.value, + builder: (BuildContext + context) => + DialogCancelSuratJalan( + cancel: + cancelSuratJalan, + deliveryID: + e.id ?? "0", + loading: + cancelLoading + .value, + date: + e.date ?? "", + deliveryNumber: + e.orderNumber ?? + "", + pic: e.pic ?? ""), + ).whenComplete(() { + onRefresh(); + }); + }, + child: Tooltip( + message: "Batalkan", + child: Icon( + EvaIcons.closeCircle, + size: 24, + color: Constant.green), + ), + ), + ], + ), + ), + ), + ) + ], + )) + .toList(), + )), + Divider(), + Container( + color: Colors.white, + height: Constant.getActualY(context: context, y: 52), + child: Row( + children: [ + Spacer(), + Text( + "Rows per page :", + style: Constant.body3_400(context: context), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + DropdownButton( + value: rowsPerPage.value, + dropdownColor: Colors.white, + elevation: 0, + style: Constant.body3_400(context: context) + .copyWith(color: Colors.black), + underline: Container(), + icon: Icon(Icons.keyboard_arrow_down_rounded), + focusColor: Colors.white, + items: [ + DropdownMenuItem( + value: 10, + child: Text("10"), + ), + DropdownMenuItem( + value: 15, + child: Text("15"), + ), + DropdownMenuItem( + value: 20, + child: Text("20"), + ), + DropdownMenuItem( + value: 30, + child: Text("30"), + ), + DropdownMenuItem( + value: 40, + child: Text("40"), + ), + DropdownMenuItem( + value: 50, + child: Text("50"), + ), + ], + onChanged: (value) { + rowsPerPage.value = value!; + }), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + Text( + "${dataStart.value} - ${dataEnd.value} of ${sjList.value.total}", + style: Constant.body3_400(context: context), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + IconButton( + iconSize: 20, + onPressed: currPage.value > 1 + ? () { + currPage.value = currPage.value - 1; + } + : null, + icon: Icon(Icons.arrow_back_ios_new_rounded)), + SizedBox( + width: Constant.getActualX(context: context, x: 10), + ), + IconButton( + iconSize: 20, + onPressed: + currPage.value < sjList.value.totalPage!.toInt() + ? () { + currPage.value = currPage.value + 1; + } + : null, + icon: Icon(Icons.arrow_forward_ios_outlined)), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ) + ], + ), + ) + ], + )); + } +} diff --git a/lib/widgets/bahan_table_widget.dart b/lib/widgets/bahan_table_widget.dart new file mode 100644 index 0000000..9370da7 --- /dev/null +++ b/lib/widgets/bahan_table_widget.dart @@ -0,0 +1,111 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../app/constant.dart'; +import '../model/registration_model.dart'; + +class BahanTableWidget extends HookConsumerWidget { + const BahanTableWidget({ + super.key, + required this.specimenLoading, + required this.tempRegistrationData, + required this.addOrderLoading, + }); + + final ValueNotifier specimenLoading; + final RegistrationModel tempRegistrationData; + final bool addOrderLoading; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Column( + children: [ + Row( + children: [ + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 20)), + width: Constant.getActualX(context: context, x: 1160) * 0.13, + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Text('Bahan', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 20)), + width: Constant.getActualX(context: context, x: 1160) * 0.11, + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Text('Jumlah', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 300), + child: specimenLoading.value + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 30), + ) + : ListView(children: [ + ...?tempRegistrationData.bahan?.map((e) => SizedBox( + height: Constant.getActualY(context: context, y: 64), + child: Row( + children: [ + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 20)), + width: Constant.getActualX( + context: context, x: 1160) * + 0.13, + color: Colors.white, + child: Chip( + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(6)), + ), + backgroundColor: Constant.blue_016, + label: Text( + '${e.name}'.replaceAll(' ', '\u00A0'), + style: Constant.caption1_600( + context: context) + .copyWith(color: Constant.textBlue)), + )), + Container( + height: Constant.getActualY( + context: context, y: 52), + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 20)), + width: Constant.getActualX( + context: context, x: 1160) * + 0.11, + child: TextField( + enabled: !addOrderLoading, + controller: e.ctr, + onChanged: (value) { + print(value); + }, + decoration: InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(8)))), + )) + ], + ), + )), + ]), + ), + ], + ); + } +} diff --git a/lib/widgets/button_registrasi.dart b/lib/widgets/button_registrasi.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/widgets/custom_dialog_builder.dart b/lib/widgets/custom_dialog_builder.dart new file mode 100644 index 0000000..5ef884b --- /dev/null +++ b/lib/widgets/custom_dialog_builder.dart @@ -0,0 +1,409 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +import '../app/constant.dart'; +import 'iframe_view.dart'; + +class CustomDialogBuilder { + // // pdf mobile + // static reportPdf( + // BuildContext context, + // String typeReportParam, + // String startDateParam, + // String endDateParam, + // String usernameParam, + // String filterRegionalID, + // String tatReportCategoryCode, + // ) { + // String urlParam = ""; + // String tatReportCategoryCodeLowerCase = tatReportCategoryCode.toLowerCase(); + // if (typeReportParam == "type_report_pendapatan") { + // // pendapatan + // // urlParam = + // // "https://${Constant.ipAddress}/birt/run?__report=report/one/rekap/rpt_sales_001.rptdesign&__format=xlsx&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&tm=${DateTime.now()}"; + + // // urlParam = + // // "https://devone.aplikasi.web.id/birt/frameset?__report=report/one/lab/rpt_test.rptdesign&__format=xls&username=ADMIN&PID=131878&tm=1697596972372"; + + // if (filterRegionalID == "0") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_sales_nasional_001.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=pdf "; + // } else { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_sales_001.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=pdf"; + // } + // } else { + // // piutang + // if (typeReportParam == "type_report_piutang") { + // // urlParam = + // // "https://${Constant.ipAddress}/birt/run?__report=report/one/rekap/rpt_sales_001.rptdesign&__format=xlsx&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&tm=${DateTime.now()}"; + + // // urlParam = + // // "https://${Constant.ipAddress}/birt/frameset?__report=report/one/lab/rpt_test.rptdesign&__format=xls&username=ADMIN&PID=131878&tm=1697596972372"; + + // if (filterRegionalID == "0") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_piutang_nasional_001.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=pdf "; + // } else { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_piutang_001.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=pdf"; + // } + // } else { + // // tat + // if (filterRegionalID == "0") { + // // global + // if (tatReportCategoryCodeLowerCase == "global") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=pdf "; + // print("global nasional tat : $urlParam"); + // } else { + // // fo + // if (tatReportCategoryCodeLowerCase == "fo") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=pdf "; + // print("fo nasional tat : $urlParam"); + // } else { + // // proses + // if (tatReportCategoryCodeLowerCase == "proses") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=pdf "; + // print("proses nasional tat : $urlParam"); + // } else { + // // sampling + // if (tatReportCategoryCodeLowerCase == "sampling") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=pdf "; + // print("sampling nasional tat : $urlParam"); + // } else { + // // pengolahan + // if (tatReportCategoryCodeLowerCase == "pengolahan") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=pdf "; + // print("sampling nasional tat : $urlParam"); + // } else { + // // validasi + // if (tatReportCategoryCodeLowerCase == "validasi") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=pdf "; + // print("sampling nasional tat : $urlParam"); + // } + // } + // } + // } + // } + // } + // } else { + // // global + // if (tatReportCategoryCodeLowerCase == "global") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001i_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=pdf"; + // print("global regional tat : $urlParam"); + // } else { + // // fo + // if (tatReportCategoryCodeLowerCase == "fo") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001i_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=pdf"; + // print("fo regional tat : $urlParam"); + // } else { + // // proses + // if (tatReportCategoryCodeLowerCase == "proses") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001i_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=pdf"; + // print("proses regional tat : $urlParam"); + // } else { + // // sampling + // if (tatReportCategoryCodeLowerCase == "sampling") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001i_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=pdf"; + // print("sampling regional tat : $urlParam"); + // } else { + // // pengolahan + // if (tatReportCategoryCodeLowerCase == "pengolahan") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001i_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=pdf"; + // print("sampling regional tat : $urlParam"); + // } else { + // // validasi + // if (tatReportCategoryCodeLowerCase == "validasi") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001i_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=pdf"; + // print("sampling regional tat : $urlParam"); + // } + // } + // } + // } + // } + // } + // } + // } + // } + + // // launchUrlString(urlParam, mode: LaunchMode.externalNonBrowserApplication); + // launchUrlString(urlParam); + // } + + // // excel web or mobile + // static reportExcel( + // String typeReportParam, + // String startDateParam, + // String endDateParam, + // String usernameParam, + // String filterRegionalID, + // String tatReportCategoryCode, + // ) { + // String urlParam = ""; + // String tatReportCategoryCodeLowerCase = tatReportCategoryCode.toLowerCase(); + // if (typeReportParam == "type_report_pendapatan") { + // // pendapatan + // // urlParam = + // // "https://${Constant.ipAddress}/birt/run?__report=report/one/rekap/rpt_sales_001.rptdesign&__format=xlsx&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&tm=${DateTime.now()}"; + + // // urlParam = + // // "https://devone.aplikasi.web.id/birt/frameset?__report=report/one/lab/rpt_test.rptdesign&__format=xls&username=ADMIN&PID=131878&tm=1697596972372"; + + // if (filterRegionalID == "0") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_sales_nasional_001.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=xls "; + // } else { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_sales_001.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=xls"; + // } + // } else { + // if (typeReportParam == "type_report_piutang") { + // // piutang + // // urlParam = + // // "https://${Constant.ipAddress}/birt/run?__report=report/one/rekap/rpt_sales_001.rptdesign&__format=xlsx&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&tm=${DateTime.now()}"; + + // // urlParam = + // // "https://${Constant.ipAddress}/birt/frameset?__report=report/one/lab/rpt_test.rptdesign&__format=xls&username=ADMIN&PID=131878&tm=1697596972372"; + + // if (filterRegionalID == "0") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_piutang_nasional_001.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=xls "; + // } else { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_piutang_001.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=xls"; + // } + // } else { + // // tat + // // if (filterRegionalID == "0") { + // // urlParam = + // // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=xls "; + // // } else { + // // urlParam = + // // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001i.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=xls"; + // // } + + // // tat + // if (filterRegionalID == "0") { + // // global + // if (tatReportCategoryCodeLowerCase == "global") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=xls "; + // print("global nasional tat : $urlParam"); + // } else { + // // fo + // if (tatReportCategoryCodeLowerCase == "fo") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=xls "; + // print("fo nasional tat : $urlParam"); + // } else { + // // proses + // if (tatReportCategoryCodeLowerCase == "proses") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=xls "; + // print("proses nasional tat : $urlParam"); + // } else { + // // sampling + // if (tatReportCategoryCodeLowerCase == "sampling") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=xls "; + // print("sampling nasional tat : $urlParam"); + // } else { + // // pengolahan + // if (tatReportCategoryCodeLowerCase == "pengolahan") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=xls "; + // print("sampling nasional tat : $urlParam"); + // } else { + // // validasi + // if (tatReportCategoryCodeLowerCase == "validasi") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&__format=xls "; + // print("sampling nasional tat : $urlParam"); + // } + // } + // } + // } + // } + // } + // } else { + // // global + // if (tatReportCategoryCodeLowerCase == "global") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001i_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=xls"; + // print("global regional tat : $urlParam"); + // } else { + // // fo + // if (tatReportCategoryCodeLowerCase == "fo") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001i_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=xls"; + // print("fo regional tat : $urlParam"); + // } else { + // // proses + // if (tatReportCategoryCodeLowerCase == "proses") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001i_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=xls"; + // print("proses regional tat : $urlParam"); + // } else { + // // sampling + // if (tatReportCategoryCodeLowerCase == "sampling") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001i_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=xls"; + // print("sampling regional tat : $urlParam"); + // } else { + // // pengolahan + // if (tatReportCategoryCodeLowerCase == "pengolahan") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001i_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=xls"; + // print("sampling regional tat : $urlParam"); + // } else { + // // validasi + // if (tatReportCategoryCodeLowerCase == "validasi") { + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_tat_nasional_001i_$tatReportCategoryCodeLowerCase.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&PregionalID=${filterRegionalID}&username=${usernameParam}&__format=xls"; + // print("sampling regional tat : $urlParam"); + // } + // } + // } + // } + // } + // } + // } + // } + // } + + // launchUrlString(urlParam); + // } + + // pdf web + static dialogBuilderWebPDF( + BuildContext context, + String startDateParam, + String endDateParam, + String noRegNamaParam, + String username, + String companyID) { + String urlParam = ""; + String titleParam = ""; + + // pendapatan + var param = { + "CompanyID": companyID, + "startDateParam": startDateParam, + "endDateParam": endDateParam, + "noRegNamaParam": noRegNamaParam + }; + + print(param); + + // Parsing tanggal dan waktu + DateTime dateTime = DateTime.now(); + + // Mengonversi ke timestamp + int timestamp = dateTime.millisecondsSinceEpoch; + + titleParam = "Report Order"; + urlParam = + "${Constant.protocol}//${Constant.ipAddress}/birt/run?__report=report/one/regional/mitra/rpt_list_order.rptdesign&__format=pdf&PStartDate=$startDateParam&PEndDate=$endDateParam&PSearch=$noRegNamaParam&username=$username&CompanyID=$companyID&tm=$timestamp"; + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/rekap/rpt_sales_001.rptdesign&__format=pdf&PStartDate=$startDateParam&PEndDate=$endDateParam&username=${usernameParam}&tm=${DateTime.now()}"; + + // urlParam = + // "https://${Constant.ipAddress}/birt/run?__report=report/one/one_dash/rpt_sales_nasional_001.rptdesign&PStartDate=$startDateParam&PEndDate=$endDateParam&noreg=${noRegNamaParam}&__format=pdf "; + + // urlParam = + // "https://devone.aplikasi.web.id/birt/frameset?__report=report/one/lab/rpt_test.rptdesign&__format=pdf&username=ADMIN&PID=131878&tm=1697596972372"; + + return showDialog( + context: context, + builder: (BuildContext context) { + return HookConsumer(builder: (context, ref, widget) { + final isLoading = useState(true); + useEffect(() { + Timer(Duration(seconds: 3), () { + isLoading.value = false; + }); + return null; + }, []); + + return AlertDialog( + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + title: Text(titleParam), + content: Padding( + padding: const EdgeInsets.all(10), + child: SizedBox( + width: Constant.getActualX(context: context, x: 1440), + child: Column( + children: [ + // Text('${startDateParam}'), + // Text('${endDateParam}'), + // Text('${noRegNamaParam}'), + Expanded( + child: Stack( + alignment: Alignment.center, + children: [ + IframeView( + key: UniqueKey(), + source: urlParam, + onCreated: (idx) { + // print('Oncreated print ${idx}'); + isLoading.value = false; + }, + ), + // loading + if (isLoading.value == true) + const Center( + child: SizedBox( + width: 32, + height: 32, + child: CircularProgressIndicator(), + ), + ), + ], + ), + ), + ], + ), + ), + ), + actions: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + textStyle: Constant.body3_600(context: context), + ), + onPressed: (isLoading.value) + ? null + : () { + isLoading.value = false; + Navigator.of(context).pop(); + }, + child: Padding( + padding: const EdgeInsets.all(10), + child: Text( + 'Tutup', + style: TextStyle(color: Colors.white), + ), + ), + ), + ], + ); + }); + }, + ); + } +} diff --git a/lib/widgets/custom_snackbar_widget.dart b/lib/widgets/custom_snackbar_widget.dart new file mode 100644 index 0000000..12fcf79 --- /dev/null +++ b/lib/widgets/custom_snackbar_widget.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:top_snackbar_flutter/custom_snack_bar.dart'; +import 'package:top_snackbar_flutter/top_snack_bar.dart'; + +enum snackbarType { error, info, success, warning } + +SanckbarWidget(BuildContext context, String msg, snackbarType tipe) { + switch (tipe) { + case snackbarType.error: + return showTopSnackBar( + animationDuration: Duration(milliseconds: 900), + displayDuration: Duration(seconds: 1), + dismissType: DismissType.onTap, + Overlay.of(context), + CustomSnackBar.error( + message: msg, + ), + ); + break; + case snackbarType.success: + return showTopSnackBar( + animationDuration: Duration(milliseconds: 900), + displayDuration: Duration(seconds: 1), + dismissType: DismissType.onTap, + Overlay.of(context), + CustomSnackBar.success( + message: msg, + ), + ); + break; + case snackbarType.info: + return showTopSnackBar( + animationDuration: Duration(milliseconds: 900), + displayDuration: Duration(seconds: 1), + dismissType: DismissType.onTap, + Overlay.of(context), + CustomSnackBar.info( + message: msg, + ), + ); + break; + case snackbarType.warning: + return showTopSnackBar( + animationDuration: Duration(milliseconds: 900), + displayDuration: Duration(seconds: 1), + dismissType: DismissType.onTap, + Overlay.of(context), + CustomSnackBar.info( + backgroundColor: Colors.orangeAccent, + message: msg, + ), + ); + break; + default: + return showTopSnackBar( + animationDuration: Duration(milliseconds: 900), + displayDuration: Duration(seconds: 1), + dismissType: DismissType.onTap, + Overlay.of(context), + CustomSnackBar.info( + message: msg, + ), + ); + } +} diff --git a/lib/widgets/custom_text_field.dart b/lib/widgets/custom_text_field.dart new file mode 100644 index 0000000..fb2ba0c --- /dev/null +++ b/lib/widgets/custom_text_field.dart @@ -0,0 +1,254 @@ +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../app/constant.dart'; + +class CustomTextField extends StatelessWidget { + final String hintText; + final String labelText; + final bool isPassword; + final int? minLines; + final int? maxLines; + final Widget? suffixIcon; + final TextInputType? inputType; + final TextEditingController? controller; + final TextStyle? hintStyle; + final TextStyle? labelStyle; + final TextStyle? style; + final bool? isDense; + final bool? disable; + final Function? onChange; + final List? inputFormater; + final String? initialValue; + final String? Function(String?)? validator; + // final String Function(String?)? validator; + + const CustomTextField( + {super.key, + required this.hintText, + required this.labelText, + this.isPassword = false, + this.maxLines = 1, + this.minLines = 1, + this.suffixIcon, + this.inputType, + this.style, + this.hintStyle, + this.labelStyle, + this.isDense, + this.disable, + this.onChange, + this.inputFormater, + this.initialValue, + this.validator, + this.controller}); + + @override + Widget build(BuildContext context) { + return TextFormField( + onChanged: (value) { + if (onChange != null) { + onChange!(value); + } + }, + enabled: disable, + controller: controller, + style: style ?? Constant.body2_400(context: context), + obscureText: isPassword, + minLines: minLines, + maxLines: maxLines, + keyboardType: inputType, + initialValue: initialValue, + validator: validator, + autovalidateMode: AutovalidateMode.always, + inputFormatters: [ + if (inputType == TextInputType.number) + FilteringTextInputFormatter.digitsOnly, + ...?inputFormater + ], + decoration: InputDecoration( + hintStyle: hintStyle ?? Constant.body2_400(context: context), + labelStyle: labelStyle ?? Constant.body2_400(context: context), + fillColor: Constant.primaryBlue, + labelText: labelText, + isDense: isDense ?? false, + contentPadding: isDense == true + ? EdgeInsets.symmetric( + vertical: Constant.getActualY(context: context, y: 8), + horizontal: Constant.getActualX(context: context, x: 10)) + : null, + + // isCollapsed: true, + hintText: hintText, + suffixIcon: suffixIcon, + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + )), + ); + } +} + +class CustomPasswordField extends HookConsumerWidget { + final String hintText; + final String labelText; + final ValueNotifier isPassword; + final int? minLines; + final int? maxLines; + final Widget? suffixIcon; + final TextEditingController? controller; + final String? Function(String?)? validator; + + const CustomPasswordField( + {super.key, + required this.hintText, + required this.labelText, + required this.isPassword, + this.maxLines = 1, + this.minLines = 1, + this.suffixIcon, + this.validator, + this.controller}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return TextFormField( + controller: controller, + style: Constant.body2_400(context: context), + obscureText: isPassword.value, + minLines: minLines, + maxLines: maxLines, + validator: validator, + autovalidateMode: AutovalidateMode.always, + decoration: InputDecoration( + hintStyle: Constant.body2_400(context: context), + labelStyle: Constant.body2_400(context: context), + fillColor: Constant.primaryBlue, + labelText: labelText, + hintText: hintText, + suffixIcon: IconButton( + icon: + Icon(!isPassword.value ? EvaIcons.eye : EvaIcons.eyeOffOutline), + onPressed: () { + isPassword.value = !isPassword.value; + }, + ), + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + )), + ); + } +} + +class CustomDropDownModel { + String? id; + String? name; + String? type; + + CustomDropDownModel({this.id, this.name, this.type}); + + CustomDropDownModel.fromJson(Map json) { + id = json['id']; + name = json['name']; + type = json['type']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['name'] = name; + data['type'] = type; + return data; + } +} + +class CustomDropDown extends HookConsumerWidget { + const CustomDropDown( + {super.key, + required this.data, + this.selectedData, + required this.onSelect, + required this.hintText, + required this.width, + required this.loading, + required this.label}); + final List data; + final ValueNotifier? selectedData; + final Function onSelect; + final String hintText; + final bool loading; + final Widget label; + final double width; + @override + Widget build(BuildContext context, WidgetRef ref) { + final selectedLokal = useState(CustomDropDownModel()); + final key = useState(10000); + // final selectedLokal = useState(CustomDropDownModel()); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + if (selectedData?.value.id != null) { + var tempIdx = CustomDropDownModel(id: "-1"); + if (data.isNotEmpty) { + tempIdx = data + .firstWhere((element) => element.id == selectedData?.value.id); + } + var selectedDataIndex = data.indexOf(tempIdx); + if (selectedDataIndex != -1) { + if (data.isNotEmpty) { + selectedLokal.value = data[selectedDataIndex]; + } + } + } + key.value = key.value + 1; + }); + return () {}; + }, [selectedData]); + selectedLokal.addListener( + () { + selectedData?.value = selectedLokal.value; + }, + ); + return DropdownMenu( + key: ValueKey(key.value), + initialSelection: selectedLokal.value, + trailingIcon: loading + ? LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 20) + : null, + width: width, + hintText: hintText, + textStyle: Constant.body2_400(context: context), + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderSide: BorderSide(color: Constant.primaryBlue), + borderRadius: BorderRadius.circular(8), + )), + label: label, + onSelected: (value) { + onSelect(value); + selectedLokal.value = value!; + }, + dropdownMenuEntries: data + .map>( + (e) => DropdownMenuEntry(value: e, label: e.name ?? "")) + .toList()); + + // return DropdownButton( + // // value: selectedData?.value, + // items: data.length > 0 + // ? data + // .map>( + // (e) => DropdownMenuItem( + // child: Text(e.name!), + // )) + // .toList() + // : [], + // onChanged: (value) => onSelect(value), + // ); + } +} diff --git a/lib/widgets/dialog_print.dart b/lib/widgets/dialog_print.dart new file mode 100644 index 0000000..cb5ece4 --- /dev/null +++ b/lib/widgets/dialog_print.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/app/constant.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +class DialogPrint extends HookConsumerWidget { + const DialogPrint({super.key, required this.url}); + final String url; + @override + Widget build(BuildContext context, WidgetRef ref) { + late WebViewController controller; + + controller = WebViewController() + ..loadRequest( + Uri.parse(url), + ); + return Padding( + padding: EdgeInsets.all(40), + child: Material( + borderRadius: BorderRadius.circular(8), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + ), + child: Column( + children: [ + Container( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 32)), + height: Constant.getActualY(context: context, y: 96), + decoration: BoxDecoration( + color: Colors.grey, + borderRadius: BorderRadius.only( + topRight: Radius.circular(8), + topLeft: Radius.circular(8)), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Cetak Surat Jalan", + style: Constant.h2_600(context: context), + ), + IconButton( + onPressed: () { + Navigator.pop(context); + }, + icon: Icon( + size: 30, + Icons.close_rounded, + color: Colors.red, + )) + ], + ), + ), + SizedBox( + // color: Colors.red, + height: Constant.getActualY(context: context, y: 800), + // width: Constant.getActualX(context: context, x: 1500), + child: WebViewWidget( + controller: controller, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/header.dart b/lib/widgets/header.dart new file mode 100644 index 0000000..49819a2 --- /dev/null +++ b/lib/widgets/header.dart @@ -0,0 +1,376 @@ +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/date_symbol_data_local.dart'; +import 'package:intl/intl.dart'; +import 'package:mitra_corporate/provider/menu_provider.dart'; +import 'package:mitra_corporate/widgets/custom_text_field.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../app/constant.dart'; +import '../app/route.dart'; +import '../provider/auth_provider.dart'; +import '../screen/login/change_password_provider.dart'; +import '../screen/login/logout_provider.dart'; +import 'custom_snackbar_widget.dart'; + +class Header extends HookConsumerWidget { + const Header({ + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final currentUser = ref.watch(authProvider); + final isExpand = ref.watch(sideBarExpandProvider); + final auth = ref.watch(authProvider); + final date = useState(""); + ref.listen(logoutProvider, (prev, next) async { + if (next is LogoutStateLoading) { + } else if (next is LogoutStateError) { + SanckbarWidget(context, next.message, snackbarType.error); + } else if (next is LogoutStateDone) { + if (next.message == "OK") { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.remove(Constant.tokenName); + Navigator.pushNamed(context, loginRoute); + } else { + SanckbarWidget(context, "Logout Gagal", snackbarType.error); + } + } + }); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + DateTime now = DateTime.now(); + initializeDateFormatting('id', '').then((_) { + date.value = DateFormat('EEEE, d MMMM yyyy', "id").format(now); + print(DateFormat('EEEE, d MMMM yyyy', "id").format(now)); + }); + }); + return () {}; + }, []); + return Container( + color: Colors.white, + alignment: Alignment.center, + width: Constant.getActualX(context: context, x: isExpand ? 1159 : 1344), + height: Constant.getActualY(context: context, y: 90), + child: Padding( + padding: + const EdgeInsets.only(top: 16, bottom: 16, left: 34, right: 34), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Center( + // child: Icon( + // EvaIcons.calendarOutline, + // // size: 24, + // size: Constant.getActualX(context: context, x: 24), + // color: Constant.primaryRed, + // ), + // ), + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Icon(EvaIcons.calendarOutline, + color: Constant.textBlack, size: 24), + ), + SizedBox(width: Constant.getActualX(context: context, x: 8)), + Padding( + padding: EdgeInsets.only(top: 8.0), + child: Text( + date.value, + style: Constant.body1_600(context: context) + .copyWith(color: Constant.textBlack), + textAlign: TextAlign.left, + ), + ), + const Spacer(), + // Icon(EvaIcons.bell, size: 28, color: Constant.primaryRed), + SizedBox( + width: Constant.getActualX(context: context, x: 24), + ), + // CircleAvatar( + // child: Icon(EvaIcons.person), + // ), + MenuAnchor( + style: MenuStyle( + backgroundColor: WidgetStatePropertyAll(Colors.white), + elevation: WidgetStatePropertyAll(5), + padding: WidgetStatePropertyAll(EdgeInsets.all(10)), + shape: WidgetStatePropertyAll(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + side: WidgetStatePropertyAll(BorderSide.none)), + builder: (BuildContext context, MenuController controller, + Widget? child) { + return InkWell( + onTap: () { + if (controller.isOpen) { + controller.close(); + } else { + controller.open(); + } + }, + child: CircleAvatar( + backgroundColor: Constant.primaryRed, + child: Icon( + EvaIcons.person, + color: Colors.white, + ), + ), + ); + }, + alignmentOffset: Offset(-190, 20), + menuChildren: [ + Container( + margin: EdgeInsets.symmetric(vertical: 20), + width: Constant.getActualX(context: context, x: 200), + child: Column( + children: [ + CircleAvatar( + backgroundColor: Constant.primaryRed, + child: Icon( + EvaIcons.person, + color: Colors.white, + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.only(bottom: 10), + child: Center( + child: Text( + auth?.mUserUsername ?? "", + style: Constant.body1_600(context: context), + ))), + MenuItemButton( + leadingIcon: Icon( + EvaIcons.logOut, + color: Constant.primaryRed, + ), + onPressed: () { + ref.read(logoutProvider.notifier).logout( + M_UserID: currentUser!.mUserID ?? "0", + M_UserUsername: currentUser.companyName ?? "0"); + }, + child: Text( + 'Logout', + style: Constant.body3_400(context: context), + ), + ), + MenuItemButton( + leadingIcon: Icon( + EvaIcons.shield, + color: Constant.primaryRed, + ), + onPressed: () { + showDialog( + barrierDismissible: false, + context: context, + builder: (BuildContext context) => AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20)), + title: Text( + 'Ubah Password', + style: Constant.body1_600(context: context), + ), + content: ChangePassword(), + ), + ); + }, + child: Text( + 'Ubah Password', + style: Constant.body3_400(context: context), + ), + ), + Container( + margin: EdgeInsets.only(top: 10, bottom: 10), + child: Center( + child: Text(Constant.version, + style: + Constant.caption1_600(context: context).copyWith( + color: Constant.textGrey, + )), + ), + ) + ]) + ], + ), + ), + ); + } +} + +class ChangePassword extends HookConsumerWidget { + const ChangePassword({ + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final auth = ref.watch(authProvider); + final currPassState = useState(true); + final newPassState = useState(true); + final newPassVerState = useState(true); + final loading = useState(false); + final updatekey = useState(0); + final currPassCtr = useTextEditingController(text: ""); + final newPassCtr = useTextEditingController(text: ""); + final newPassVerCtr = useTextEditingController(text: ""); + newPassVerCtr.addListener(() { + updatekey.value = updatekey.value + 1; + }); + changePassword() { + ref.read(ChangePasswordProvider.notifier).ChangePassword( + token: auth?.token ?? "", + current_password: currPassCtr.text, + new_password: newPassCtr.text, + password_confirmation: newPassVerCtr.text); + } + + ref.listen(ChangePasswordProvider, (prev, next) async { + if (next is ChangePasswordStateInit) { + loading.value = true; + } else if (next is ChangePasswordStateLoading) { + loading.value = true; + } else if (next is ChangePasswordStateError) { + SanckbarWidget(context, next.message, snackbarType.error); + loading.value = false; + Constant.autoLogout(context: context, msg: next.message); + } else if (next is ChangePasswordStateDone) { + // if (next.message == "OK") { + // final SharedPreferences prefs = await SharedPreferences.getInstance(); + // await prefs.remove(Constant.tokenName); + // Navigator.pushNamed(context, loginRoute); + // } else { + // SanckbarWidget(context, "ChangePassword Gagal", snackbarType.error); + // } + ref.read(logoutProvider.notifier).logout( + M_UserID: auth!.mUserID ?? "0", + M_UserUsername: auth.companyName ?? "0"); + SanckbarWidget(context, next.message, snackbarType.success); + } + loading.value = false; + }); + + return SizedBox( + height: Constant.getActualY(context: context, y: 370), + width: Constant.getActualX(context: context, x: 300), + child: Column( + children: [ + CustomPasswordField( + controller: currPassCtr, + isPassword: currPassState, + hintText: "Current Password", + labelText: "Current password"), + SizedBox( + height: Constant.getActualY(context: context, y: 24), + ), + CustomPasswordField( + controller: newPassCtr, + isPassword: newPassState, + validator: (String? value) { + if (value != null) { + if (value.isNotEmpty) { + // if (value != newPassVerCtr.text) { + // return "verifikasi password salah"; + // } + if (!value.contains(RegExp(r'[A-Z]'))) { + return " password harus mengandung 1 huruf besar"; + } + if (!value.contains(RegExp(r'[a-z]'))) { + return " password harus mengandung 1 huruf kecil"; + } + if (!value.contains(RegExp(r'[0-9]'))) { + return " password harus mengandung 1 angka"; + } + if (value.length < 8) { + return " password minimal 8 digit"; + } + } + } + + return null; + }, + hintText: "New Password", + labelText: "New password"), + SizedBox( + height: Constant.getActualY(context: context, y: 24), + ), + CustomPasswordField( + controller: newPassVerCtr, + isPassword: newPassVerState, + validator: (String? value) { + if (value != null) { + if (value.isNotEmpty) { + if (value != newPassCtr.text) { + return "verifikasi password salah"; + } + } + } + + return null; + }, + hintText: "New Password verification", + labelText: "New Password verification"), + Spacer(), + Row( + children: [ + Spacer(), + OutlinedButton( + style: OutlinedButton.styleFrom( + shape: RoundedRectangleBorder( + side: + BorderSide(color: Constant.primaryRed, width: 2), + borderRadius: BorderRadius.circular(8))), + onPressed: !loading.value + ? () { + Navigator.pop(context); + } + : null, + child: Container( + margin: EdgeInsets.symmetric(vertical: 8), + child: Text( + "Batal", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.primaryRed), + ), + )), + SizedBox( + width: 10, + ), + ElevatedButton( + key: ValueKey(updatekey.value), + onPressed: currPassCtr.text.isNotEmpty && + newPassCtr.text.isNotEmpty && + newPassVerCtr.text.isNotEmpty && + newPassCtr.text == newPassVerCtr.text && + loading.value == false + ? () { + changePassword(); + } + : null, + style: ElevatedButton.styleFrom( + backgroundColor: Constant.primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8))), + child: Container( + margin: EdgeInsets.symmetric(vertical: 8), + child: loading.value + ? Center( + child: LoadingAnimationWidget.staggeredDotsWave( + color: Constant.primaryRed, size: 24), + ) + : Text("Simpan", + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textWhite)), + )) + ], + ) + ], + ), + ); + } +} diff --git a/lib/widgets/iframe_view.dart b/lib/widgets/iframe_view.dart new file mode 100644 index 0000000..f5a7f92 --- /dev/null +++ b/lib/widgets/iframe_view.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'dart:html'; +import 'dart:ui' as ui; + +class IframeView extends StatefulWidget { + final String source; + final void Function(int)? onCreated; + const IframeView({ + super.key, + required this.source, + this.onCreated, + }); + + @override + _IframeViewState createState() => _IframeViewState(); +} + +class _IframeViewState extends State { + // Widget _iframeWidget; + final IFrameElement _iframeElement = IFrameElement(); + + @override + void initState() { + super.initState(); + _iframeElement.style.border = 'none'; + _iframeElement.onLoad.listen((event) { + if (widget.onCreated != null && event.type == "load") { + widget.onCreated!(0); + } + }); + _iframeElement.src = widget.source; + //ignore: undefined_prefixed_name + ui.platformViewRegistry.registerViewFactory( + widget.source, //use source as registered key to ensure uniqueness + (int viewId) => _iframeElement, + ); + } + + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return HtmlElementView( + key: UniqueKey(), + viewType: widget.source, + ); + } +} diff --git a/lib/widgets/order_status_icon.dart b/lib/widgets/order_status_icon.dart new file mode 100644 index 0000000..77488ec --- /dev/null +++ b/lib/widgets/order_status_icon.dart @@ -0,0 +1,38 @@ +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; + +class OrderStatusIcon extends StatelessWidget { + const OrderStatusIcon({super.key, required this.status}); + final String status; + @override + Widget build(BuildContext context) { + switch (status) { + case "N": + return Tooltip( + message: "Belum dikirim", + child: Icon( + EvaIcons.stopCircle, + color: Colors.blue, + ), + ); + case "P": + return Tooltip( + message: "Pending", + child: Icon( + EvaIcons.clock, + color: Colors.yellow, + ), + ); + case "S": + return Tooltip( + message: "Terkirim", + child: Icon( + EvaIcons.checkmarkCircle2, + color: Colors.green, + ), + ); + default: + return const Placeholder(); + } + } +} diff --git a/lib/widgets/progress_stepper.dart b/lib/widgets/progress_stepper.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/widgets/qr_status_chip.dart b/lib/widgets/qr_status_chip.dart new file mode 100644 index 0000000..9fd70fa --- /dev/null +++ b/lib/widgets/qr_status_chip.dart @@ -0,0 +1,38 @@ +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; + +class QRStatusChip extends StatelessWidget { + const QRStatusChip({super.key, required this.status}); + final String status; + @override + Widget build(BuildContext context) { + switch (status) { + case "N": + return Tooltip( + message: "Belum dikirim", + child: Icon( + EvaIcons.stopCircle, + color: Colors.blue, + ), + ); + case "P": + return Tooltip( + message: "Pending", + child: Icon( + EvaIcons.clock, + color: Colors.yellow, + ), + ); + case "S": + return Tooltip( + message: "Terkirim", + child: Icon( + EvaIcons.checkmarkCircle2, + color: Colors.green, + ), + ); + default: + return const Placeholder(); + } + } +} diff --git a/lib/widgets/side_menu.dart b/lib/widgets/side_menu.dart new file mode 100644 index 0000000..08be8e2 --- /dev/null +++ b/lib/widgets/side_menu.dart @@ -0,0 +1,151 @@ +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/app/route.dart'; +import 'package:mitra_corporate/model/menu_model.dart'; +import 'package:mitra_corporate/provider/auth_provider.dart'; +import 'package:mitra_corporate/provider/menu_provider.dart'; +import 'package:mitra_corporate/screen/login/logout_provider.dart'; +import 'package:mitra_corporate/widgets/side_menu_btn_widget.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../app/constant.dart'; +import 'custom_snackbar_widget.dart'; + +class SideMenu extends HookConsumerWidget { + const SideMenu({ + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final currentMenu = ref.watch(currentMenuProvider); + final isExpand = ref.watch(sideBarExpandProvider); + final List menuList = Constant.getMenuModel(); + + return Container( + color: Colors.white, + child: isExpand + ? Column( + children: [ + Expanded( + child: SizedBox( + width: Constant.getActualX(context: context, x: 280), + height: Constant.getActualY(context: context, y: 90), + child: Padding( + padding: const EdgeInsets.only( + left: 24, right: 24, top: 12, bottom: 12), + child: Row( + children: [ + Image.asset('images/logo_pramita.png', + width: Constant.getActualX( + context: context, x: 147), + height: Constant.getActualY( + context: context, y: 46)), + Spacer(), + IconButton( + onPressed: () { + ref.read(sideBarExpandProvider.notifier).state = + !isExpand; + // ref.read(logoutProvider.notifier).logout( + // M_UserID: currentUser!.mUserID ?? "0", + // M_UserUsername: currentUser.companyName ?? "0"); + }, + icon: Icon(EvaIcons.menu2, + color: Constant.primaryRed, + size: Constant.getActualX( + context: context, x: 24)), + ) + ], + ), + )), + ), + SizedBox( + width: Constant.getActualX(context: context, x: 280), + height: Constant.getActualY(context: context, y: 930), + child: Padding( + padding: const EdgeInsets.all(24.0), + child: ListView( + children: menuList + .map( + (e) => Container( + margin: EdgeInsets.only( + bottom: Constant.getActualY( + context: context, y: 24)), + decoration: BoxDecoration( + color: e.subValue.contains(currentMenu) + ? Constant.selectedMenuBg + : null, + borderRadius: BorderRadius.circular(8)), + child: sideBarMenuBtnWidget( + currentMenu: currentMenu, + title: e.title, + value: e.subValue, + iconMenu: e.icon, + selectedColor: Constant.selectedMenuBg, + selectedColorText: Constant.primaryRed, + onTap: () { + ref.read(currentMenuProvider.notifier).state = + e.mainValue; + // Navigator.pushNamed(context, dashboardRoute); + }, + ), + ), + ) + .toList(), + ), + ), + ), + ], + ) + : Column( + children: [ + Expanded( + child: SizedBox( + width: Constant.getActualX(context: context, x: 95), + height: Constant.getActualY(context: context, y: 90), + child: Center( + child: IconButton( + onPressed: () { + ref.read(sideBarExpandProvider.notifier).state = + !isExpand; + }, + icon: Icon(EvaIcons.menu2, + color: Constant.primaryRed, + size: + Constant.getActualX(context: context, x: 24)), + ), + )), + ), + SizedBox( + // color: Colors.red, + width: Constant.getActualX(context: context, x: 95), + height: Constant.getActualY(context: context, y: 930), + child: Padding( + padding: const EdgeInsets.all(24.0), + child: ListView( + children: menuList + .map( + (e) => SideBarBtnSmallWidget( + title: e.title, + currentMenu: currentMenu, + value: e.subValue, + iconMenu: e.icon, + selectedColor: Constant.selectedMenuBg, + selectedColorText: Constant.primaryRed, + onTap: () { + ref.read(currentMenuProvider.notifier).state = + e.mainValue; + // Navigator.pushNamed(context, dashboardRoute); + }, + ), + ) + .toList(), + ), + ), + ) + ], + ), + ); + } +} diff --git a/lib/widgets/side_menu_btn_widget.dart b/lib/widgets/side_menu_btn_widget.dart new file mode 100644 index 0000000..85d8246 --- /dev/null +++ b/lib/widgets/side_menu_btn_widget.dart @@ -0,0 +1,105 @@ +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/provider/menu_provider.dart'; + +import '../app/constant.dart'; + +class sideBarMenuBtnWidget extends HookConsumerWidget { + const sideBarMenuBtnWidget({ + super.key, + required this.currentMenu, + required this.value, + required this.selectedColor, + required this.onTap, + required this.title, + required this.selectedColorText, + this.iconMenu, + }); + + final int currentMenu; + final String title; + final List value; + final Color selectedColor; + final Color selectedColorText; + final Function onTap; + final IconData? iconMenu; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final isExpand = ref.watch(sideBarExpandProvider); + final isSelected = useState(false); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + isSelected.value = value.any((element) => element == currentMenu); + }); + return () {}; + }, [currentMenu, value]); + return Container( + child: ListTile( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8))), + selected: isSelected.value ? true : false, + selectedTileColor: selectedColor, + title: Text(title, + style: Constant.body3_400(context: context).copyWith( + color: isSelected.value ? selectedColorText : Colors.grey)), + leading: Icon(iconMenu ?? EvaIcons.grid, + color: isSelected.value ? selectedColorText : Colors.grey, + size: 24.0), + onTap: () { + onTap(); + }, + ), + ); + } +} + +class SideBarBtnSmallWidget extends StatelessWidget { + const SideBarBtnSmallWidget({ + super.key, + required this.currentMenu, + required this.value, + required this.selectedColor, + required this.onTap, + required this.title, + required this.selectedColorText, + this.iconMenu, + }); + + final int currentMenu; + final List value; + final Color selectedColor; + final Color selectedColorText; + final String title; + final Function onTap; + final IconData? iconMenu; + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.all(5), + margin: + EdgeInsets.only(bottom: Constant.getActualY(context: context, y: 24)), + decoration: BoxDecoration( + color: value.any((element) => element == currentMenu) + ? selectedColor + : null, + borderRadius: BorderRadius.circular(8)), + child: IconButton( + onPressed: () { + onTap(); + }, + icon: Tooltip( + message: title, + child: Icon(iconMenu ?? EvaIcons.grid, + color: value.any((element) => element == currentMenu) + ? selectedColorText + : Colors.grey, + size: 24), + ), + ), + ); + } +} diff --git a/lib/widgets/specimen_table_widget.dart b/lib/widgets/specimen_table_widget.dart new file mode 100644 index 0000000..288ea9f --- /dev/null +++ b/lib/widgets/specimen_table_widget.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; + +import '../app/constant.dart'; +import '../model/registration_model.dart'; + +class SpecimenTableWidget extends HookConsumerWidget { + const SpecimenTableWidget({ + super.key, + required this.specimenLoading, + required this.tempRegistrationData, + required this.addOrderLoading, + }); + + final ValueNotifier specimenLoading; + final RegistrationModel tempRegistrationData; + final bool addOrderLoading; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Column( + children: [ + Row( + children: [ + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 20)), + width: Constant.getActualX(context: context, x: 1160) * 0.13, + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Text('Specimen', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 20)), + width: Constant.getActualX(context: context, x: 1160) * 0.11, + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Text('Jumlah', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 300), + child: specimenLoading.value + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 30), + ) + : ListView(children: [ + ...?tempRegistrationData.specimens?.map((e) => SizedBox( + height: Constant.getActualY(context: context, y: 64), + child: Row( + children: [ + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 20)), + width: Constant.getActualX( + context: context, x: 1160) * + 0.13, + color: Colors.white, + child: Chip( + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(6)), + ), + backgroundColor: Constant.yellow_016, + label: Text( + '${e.name}'.replaceAll(' ', '\u00A0'), + style: Constant.caption1_600( + context: context) + .copyWith( + color: Constant.textYellow)), + )), + Container( + height: Constant.getActualY( + context: context, y: 52), + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 20)), + width: Constant.getActualX( + context: context, x: 1160) * + 0.11, + child: TextField( + enabled: !addOrderLoading, + controller: e.ctr, + onChanged: (value) { + print(value); + }, + decoration: InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(8))), + ), + )) + ], + ), + )), + ]), + ), + ], + ); + } +} diff --git a/lib/widgets/stepper_edit.dart b/lib/widgets/stepper_edit.dart new file mode 100644 index 0000000..6a1fd66 --- /dev/null +++ b/lib/widgets/stepper_edit.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/provider/order_provider.dart'; +import 'package:mitra_corporate/provider/registrasi_provider.dart'; + +import '../app/constant.dart'; + +class StepperEdit extends HookConsumerWidget { + final int _currentStep = 0; + StepperType stepperType = StepperType.horizontal; + + StepperEdit({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final step = ref.watch(EditStepProvider); + final tempRegistrationData = ref.watch(registrationDataProvider); + final selectedTest = ref.watch(selectedTestProvider); + final orderLoading = ref.watch(addOrderLoadingProvider); + + return Scaffold( + body: Stepper( + elevation: 0, + type: stepperType, + physics: const ScrollPhysics(), + currentStep: step, + onStepTapped: !orderLoading + ? (value) { + if ((value) != 3) { + ref.read(registrasiProvider.notifier).state = value; + } + } + : null, + steps: [ + Step( + title: Text('Data Pasien', + style: Constant.caption1_400(context: context) + .copyWith(color: Constant.textBlack)), + content: Container(), + isActive: step == 0, + state: tempRegistrationData.patientData?.name != null + ? StepState.complete + : StepState.disabled, + ), + Step( + title: Text('Pemeriksaan', + style: Constant.caption1_400(context: context) + .copyWith(color: Constant.textBlack)), + content: Container(), + isActive: step == 1, + state: tempRegistrationData.tests != null + ? StepState.complete + : StepState.disabled, + ), + Step( + title: Text('Detail Order', + style: Constant.caption1_400(context: context) + .copyWith(color: Constant.textBlack)), + content: Container(), + isActive: step == 2, + state: step - 1 >= 2 ? StepState.complete : StepState.disabled, + ), + ])); + } +} diff --git a/lib/widgets/stepper_registrasi.dart b/lib/widgets/stepper_registrasi.dart new file mode 100644 index 0000000..16257b0 --- /dev/null +++ b/lib/widgets/stepper_registrasi.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mitra_corporate/provider/registrasi_provider.dart'; + +import '../app/constant.dart'; + +class StepperRegistrasi extends HookConsumerWidget { + final int _currentStep = 0; + StepperType stepperType = StepperType.horizontal; + + StepperRegistrasi({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final step = ref.watch(registrasiProvider); + final tempRegistrationData = ref.watch(registrationDataProvider); + final selectedTest = ref.watch(selectedTestProvider); + final orderLoading = ref.watch(addOrderLoadingProvider); + + return Scaffold( + body: Stepper( + elevation: 0, + type: stepperType, + physics: const ScrollPhysics(), + currentStep: step, + onStepTapped: !orderLoading + ? (value) { + if ((value) != 3) { + ref.read(registrasiProvider.notifier).state = value; + } + } + : null, + steps: [ + Step( + title: Text('Data Pasien', + style: Constant.caption1_400(context: context) + .copyWith(color: Constant.textBlack)), + content: Container(), + isActive: step == 0, + state: tempRegistrationData.patientData?.name != null + ? StepState.complete + : StepState.disabled, + ), + Step( + title: Text('Pemeriksaan', + style: Constant.caption1_400(context: context) + .copyWith(color: Constant.textBlack)), + content: Container(), + isActive: step == 1, + state: tempRegistrationData.tests != null + ? StepState.complete + : StepState.disabled, + ), + Step( + title: Text('Detail Order', + style: Constant.caption1_400(context: context) + .copyWith(color: Constant.textBlack)), + content: Container(), + isActive: step == 2, + state: step - 1 >= 2 ? StepState.complete : StepState.disabled, + ), + ])); + } +} diff --git a/lib/widgets/tests_tabel_edit_widget.dart b/lib/widgets/tests_tabel_edit_widget.dart new file mode 100644 index 0000000..597210e --- /dev/null +++ b/lib/widgets/tests_tabel_edit_widget.dart @@ -0,0 +1,574 @@ +import 'dart:ui'; + +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/intl.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; +import 'package:mask_text_input_formatter/mask_text_input_formatter.dart'; + +import '../app/constant.dart'; +import '../model/registration_model.dart'; + +class TestsTableEditWidget extends HookConsumerWidget { + const TestsTableEditWidget({ + super.key, + required this.paketDelete, + required this.specimenLoading, + required this.testScrollCtr, + required this.tempRegistrationData, + required this.addOrderLoading, + required this.delete, + required this.pickDateTime, + }); + + final ValueNotifier specimenLoading; + final ScrollController testScrollCtr; + final RegistrationModel tempRegistrationData; + final bool addOrderLoading; + final Function(String id, String idtab) delete; + final Function(String idtab, String idtest, String dt) pickDateTime; + final Function(String id) paketDelete; + + @override + Widget build(BuildContext context, WidgetRef ref) { + var maskFormatter = MaskTextInputFormatter( + initialText: + DateFormat('dd-MM-yyyy HH:mm').format(DateTime.now()).toString(), + mask: '##-##-#### ##:##', + filter: {"#": RegExp(r'[0-9]')}, + type: MaskAutoCompletionType.eager); + bool isDate(String str) { + if (DateTime.tryParse(str) != null) { + return true; + } else { + return false; + } + // try { + // DateTime.tryParse(str); + // return true; + // } catch (e) { + // return false; + // } + } + + formatDate(String date) { + String formated = ""; + if (isDate(date)) { + formated = DateFormat('dd-MM-yyyy HH:mm') + .format(DateTime.parse(date)) + .toString(); + } else { + formated = date; + } + return formated; + } + + String? validateDate(String? value) { + if (value == null || value.isEmpty) { + return "tidak boleh ksosng"; + } + final splitted = value.split(" "); + if (splitted.length == 2) { + final date = splitted[0]; + final time = splitted[1]; + final splittedDate = date.split("-"); + final splittedTime = time.split(":"); + if (splittedDate.length == 3) { + final day = int.tryParse(splittedDate[0]); + final month = int.tryParse(splittedDate[1]); + final year = int.tryParse(splittedDate[2]); + if (day != null) { + if (day > 31) { + return "Format salah"; + } + } + if (month != null) { + if (month > 12) { + return "Format salah"; + } + } + // if (year == null) { + // return "Format salah"; + // } + } else { + return "Format salah"; + } + if (splittedTime.length == 2) { + var hour = int.tryParse(splittedTime[0]); + var sec = int.tryParse(splittedTime[1]); + if (hour != null) { + if (hour > 24) { + return "Format salah"; + } + } + if (sec != null) { + if (sec > 60) { + return "Format salah"; + } + } + } else { + return "Format salah"; + } + } else { + return "Format salah"; + } + return null; + } + + return Column( + children: [ + Row( + children: [ + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 20)), + width: Constant.getActualX(context: context, x: 1160) * 0.9, + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Text('Pemeriksaan', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + // Container( + // alignment: Alignment.centerLeft, + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX(context: context, x: 20)), + // width: Constant.getActualX(context: context, x: 1160) * 0.20, + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + // child: Text('Jenis Specimen', + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textGrey)), + // ), + // Container( + // alignment: Alignment.centerLeft, + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX(context: context, x: 20)), + // width: Constant.getActualX(context: context, x: 1160) * 0.13, + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + // child: Text('Harga', + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textGrey)), + // ), + // Container( + // alignment: Alignment.centerLeft, + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX(context: context, x: 20)), + // width: Constant.getActualX(context: context, x: 1160) * 0.4, + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + // child: Text('Waktu Pengambilan Sample', + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textGrey)), + // ), + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 20)), + width: Constant.getActualX(context: context, x: 1160) * 0.1, + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Text('Aksi', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 390), + // color: Colors.red, + child: specimenLoading.value + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 40), + ) + : Scrollbar( + thumbVisibility: true, + trackVisibility: true, + scrollbarOrientation: ScrollbarOrientation.right, + controller: testScrollCtr, + child: ScrollConfiguration( + behavior: ScrollBehavior().copyWith(dragDevices: { + PointerDeviceKind.mouse, + PointerDeviceKind.touch, + PointerDeviceKind.trackpad, + PointerDeviceKind.stylus, + PointerDeviceKind.unknown + }), + child: ListView( + shrinkWrap: true, + addAutomaticKeepAlives: true, + controller: testScrollCtr, + // semanticChildCount: + // tempRegistrationData.tests?.length, + children: [ + Column( + children: tempRegistrationData.paket!.map((e) { + var index = tempRegistrationData.paket?.indexOf(e); + return Column( + children: [ + Row( + children: [ + Container( + margin: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 16), + bottom: Constant.getActualY( + context: context, y: 16)), + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 20)), + width: Constant.getActualX( + context: context, x: 1160) * + 0.9, + constraints: BoxConstraints( + minHeight: Constant.getActualY( + context: context, y: 64)), + // height: Constant.getActualY( + // context: context, y: 64), + color: Colors.white, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text(e.name ?? '', + style: Constant.body3_400( + context: context) + .copyWith( + color: + Constant.textBlack)), + SizedBox( + height: 4, + ), + Text(e.detail ?? '', + style: Constant.caption2_400( + context: context) + .copyWith( + color: + Constant.textGrey)), + ], + ), + ), + Container( + margin: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 16), + bottom: Constant.getActualY( + context: context, y: 16)), + + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 20)), + // color: Colors.red, + width: Constant.getActualX( + context: context, x: 1160) * + 0.1, + child: IconButton( + onPressed: !addOrderLoading + ? () { + // delete(e.id!, e.tab!); + paketDelete(e.id!); + } + : null, + icon: Icon(EvaIcons.trash2, + size: 24, + color: Constant.textGrey)), + ) + ], + ), + Divider() + ], + ); + }).toList(), + ), + Column( + children: tempRegistrationData.tests!.map((e) { + var index = tempRegistrationData.tests?.indexOf(e); + return Column( + children: [ + Row( + children: [ + Container( + margin: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 16), + bottom: Constant.getActualY( + context: context, y: 16)), + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 20)), + width: Constant.getActualX( + context: context, x: 1160) * + 0.9, + constraints: BoxConstraints( + minHeight: Constant.getActualY( + context: context, y: 64)), + // height: Constant.getActualY( + // context: context, y: 64), + color: Colors.white, + child: Text(e.name ?? '', + style: Constant.body3_400( + context: context) + .copyWith( + color: Constant.textBlack)), + ), + // Container( + // // alignment: Alignment.centerLeft, + // margin: EdgeInsets.only( + // bottom: Constant.getActualY( + // context: context, y: 20)), + // alignment: Alignment.centerLeft, + // padding: EdgeInsets.symmetric( + // vertical: Constant.getActualY( + // context: context, y: 10), + // horizontal: Constant.getActualX( + // context: context, x: 20)), + // width: Constant.getActualX( + // context: context, x: 1160) * + // 0.20, + // // height: Constant.getActualY( + // // context: context, y: 64), + // color: Colors.white, + // child: Column( + // // spacing: 10, + // // runSpacing: 10, + + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // ...?e.specimen + // ?.map( + // (e) => Container( + // margin: EdgeInsets.only( + // bottom: 10), + // child: Chip( + // shape: + // RoundedRectangleBorder( + // borderRadius: + // BorderRadius.all( + // Radius.circular( + // 6)), + // ), + // backgroundColor: + // Constant.yellow_016, + // label: Text( + // '${e.name} '.replaceAll( + // ' ', '\u00A0'), + // overflow: TextOverflow + // .ellipsis, + // style: Constant + // .caption1_600( + // context: + // context) + // .copyWith( + // color: Constant + // .textYellow)), + // ), + // ), + // ) + // .toList(), + // ...?e.bahan + // ?.map( + // (e) => Container( + // margin: EdgeInsets.only( + // bottom: 10), + // child: Chip( + // shape: + // RoundedRectangleBorder( + // borderRadius: + // BorderRadius.all( + // Radius.circular( + // 6)), + // ), + // backgroundColor: + // Constant.blue_016, + // label: Text( + // '${e.name} '.replaceAll( + // ' ', '\u00A0'), + // overflow: TextOverflow + // .ellipsis, + // style: Constant + // .caption1_600( + // context: + // context) + // .copyWith( + // color: Constant + // .textBlue)), + // ), + // ), + // ) + // .toList(), + // ])), + // Container( + // margin: EdgeInsets.only( + // bottom: Constant.getActualY( + // context: context, y: 20)), + + // // alignment: Alignment.centerLeft, + // alignment: Alignment.centerRight, + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX( + // context: context, x: 20)), + // width: Constant.getActualX( + // context: context, x: 1160) * + // 0.13, + // // height: Constant.getActualY( + // // context: context, y: 64), + // color: Colors.white, + // child: Text(Constant.convertToIdr( + // int.parse(e.price ?? "0"), 0))), + // Container( + // margin: EdgeInsets.only( + // bottom: Constant.getActualY( + // context: context, y: 20)), + // alignment: Alignment.centerLeft, + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX( + // context: context, x: 20)), + // width: Constant.getActualX( + // context: context, x: 1160) * + // 0.4, + // color: Colors.white, + // child: SizedBox( + // width: Constant.getActualX( + // context: context, x: 250), + // child: CustomTextField( + // suffixIcon: Icon(EvaIcons.calendar), + // initialValue: formatDate(e.date!), + // onChange: (value) { + // pickDateTime( + // e.tab!, + // e.id!, + // value, + // ); + // }, + // validator: validateDate, + // inputFormater: [ + // MaskTextInputFormatter( + // initialText: e.date, + // mask: '##-##-#### ##:##', + // filter: {"#": RegExp(r'[0-9]')}, + // type: MaskAutoCompletionType + // .eager) + // ], + + // // controller: + // // TextEditingController(text: e.date), + // // controller: searchCtr, + // isPassword: false, + // hintText: "dd-mm-yyyy hh:mm", + // labelText: "dd-mm-yyyy hh:mm", + // ), + // ) + // // InkWell( + // // onTap: !addOrderLoading + // // ? () { + // // pickDateTime( + // // e.tab!, + // // e.id!, + // // e.date!, + // // ); + // // } + // // : null, + // // child: Chip( + // // shape: RoundedRectangleBorder( + // // borderRadius: BorderRadius.all( + // // Radius.circular(6)), + // // ), + // // backgroundColor: Constant.grey_200, + // // label: Text( + // // DateFormat('dd MMM yyyy HH:mm') + // // .format(DateTime.parse(e.date!)), + // // style: Constant.caption1_600( + // // context: context) + // // .copyWith( + // // color: Constant.textGrey)), + // // ), + // // ), + // ), + Container( + margin: EdgeInsets.only( + bottom: Constant.getActualY( + context: context, y: 20)), + + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 20)), + // color: Colors.red, + width: Constant.getActualX( + context: context, x: 1160) * + 0.1, + child: IconButton( + onPressed: !addOrderLoading + ? () { + delete(e.id!, e.tab!); + } + : null, + icon: Icon(EvaIcons.trash2, + size: 24, + color: Constant.textGrey)), + ) + ], + ), + Divider() + ], + ); + }).toList(), + ), + // Container( + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX( + // context: context, x: 20)), + // child: Divider()), + // Row( + // children: [ + // Container( + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX( + // context: context, x: 20), + // vertical: Constant.getActualY( + // context: context, y: 16)), + // width: Constant.getActualX( + // context: context, x: 1160) * + // 0.34, + // child: Text( + // "TOTAL", + // style: Constant.body3_500(context: context), + // ), + // ), + // Container( + // alignment: Alignment.centerRight, + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX( + // context: context, x: 20), + // vertical: Constant.getActualY( + // context: context, y: 16)), + // width: Constant.getActualX( + // context: context, x: 1160) * + // 0.13, + // child: Text( + // Constant.convertToIdr( + // double.parse( + // tempRegistrationData.total ?? "0"), + // 0), + // style: Constant.body3_500(context: context), + // ), + // ) + // ], + // ) + ], + ), + ), + ), + ), + ], + ); + } +} diff --git a/lib/widgets/tests_tabel_widget.dart b/lib/widgets/tests_tabel_widget.dart new file mode 100644 index 0000000..06bc757 --- /dev/null +++ b/lib/widgets/tests_tabel_widget.dart @@ -0,0 +1,422 @@ +import 'dart:ui'; + +import 'package:eva_icons_flutter/eva_icons_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:intl/intl.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; +import 'package:mask_text_input_formatter/mask_text_input_formatter.dart'; + +import '../app/constant.dart'; +import '../model/registration_model.dart'; + +class TestsTableWidget extends HookConsumerWidget { + const TestsTableWidget({ + super.key, + required this.specimenLoading, + required this.testScrollCtr, + required this.tempRegistrationData, + required this.addOrderLoading, + required this.deletePaket, + required this.delete, + required this.pickDateTime, + }); + + final ValueNotifier specimenLoading; + final ScrollController testScrollCtr; + final RegistrationModel tempRegistrationData; + final bool addOrderLoading; + final Function(String id, String idtab) delete; + final Function(String id) deletePaket; + final Function(String idtab, String idtest, String dt) pickDateTime; + + @override + Widget build(BuildContext context, WidgetRef ref) { + var maskFormatter = MaskTextInputFormatter( + initialText: + DateFormat('dd-MM-yyyy HH:mm').format(DateTime.now()).toString(), + mask: '##-##-#### ##:##', + filter: {"#": RegExp(r'[0-9]')}, + type: MaskAutoCompletionType.eager); + bool isDate(String str) { + if (DateTime.tryParse(str) != null) { + return true; + } else { + return false; + } + // try { + // DateTime.tryParse(str); + // return true; + // } catch (e) { + // return false; + // } + } + + formatDate(String date) { + String formated = ""; + if (isDate(date)) { + formated = DateFormat('dd-MM-yyyy HH:mm') + .format(DateTime.parse(date)) + .toString(); + } else { + formated = date; + } + return formated; + } + + String? validateDate(String? value) { + if (value == null || value.isEmpty) { + return "tidak boleh ksosng"; + } + final splitted = value.split(" "); + if (splitted.length == 2) { + final date = splitted[0]; + final time = splitted[1]; + final splittedDate = date.split("-"); + final splittedTime = time.split(":"); + if (splittedDate.length == 3) { + final day = int.tryParse(splittedDate[0]); + final month = int.tryParse(splittedDate[1]); + final year = int.tryParse(splittedDate[2]); + if (day != null) { + if (day > 31) { + return "Format salah"; + } + } + if (month != null) { + if (month > 12) { + return "Format salah"; + } + } + // if (year == null) { + // return "Format salah"; + // } + } else { + return "Format salah"; + } + if (splittedTime.length == 2) { + var hour = int.tryParse(splittedTime[0]); + var sec = int.tryParse(splittedTime[1]); + if (hour != null) { + if (hour > 24) { + return "Format salah"; + } + } + if (sec != null) { + if (sec > 60) { + return "Format salah"; + } + } + } else { + return "Format salah"; + } + } else { + return "Format salah"; + } + return null; + } + + return Column( + children: [ + Row( + children: [ + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 20)), + width: Constant.getActualX(context: context, x: 1160) * 0.9, + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Text('Pemeriksaan', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + // Container( + // alignment: Alignment.centerLeft, + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX(context: context, x: 20)), + // width: Constant.getActualX(context: context, x: 1160) * 0.4, + // height: Constant.getActualY(context: context, y: 64), + // color: Constant.grey_200, + // child: Text('Waktu Pengambilan Sample', + // style: Constant.body3_600(context: context) + // .copyWith(color: Constant.textGrey)), + // ), + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX(context: context, x: 20)), + width: Constant.getActualX(context: context, x: 1160) * 0.1, + height: Constant.getActualY(context: context, y: 64), + color: Constant.grey_200, + child: Text('Aksi', + style: Constant.body3_600(context: context) + .copyWith(color: Constant.textGrey)), + ), + ], + ), + SizedBox( + height: Constant.getActualY(context: context, y: 390), + // color: Colors.red, + child: specimenLoading.value + ? Center( + child: LoadingAnimationWidget.discreteCircle( + color: Constant.primaryBlue, size: 40), + ) + : Scrollbar( + thumbVisibility: true, + trackVisibility: true, + scrollbarOrientation: ScrollbarOrientation.right, + controller: testScrollCtr, + child: ScrollConfiguration( + behavior: ScrollBehavior().copyWith(dragDevices: { + PointerDeviceKind.mouse, + PointerDeviceKind.touch, + PointerDeviceKind.trackpad, + PointerDeviceKind.stylus, + PointerDeviceKind.unknown + }), + child: ListView( + shrinkWrap: true, + addAutomaticKeepAlives: true, + controller: testScrollCtr, + // semanticChildCount: + // tempRegistrationData.tests?.length, + children: [ + Column( + children: tempRegistrationData.paket!.map((e) { + var index = tempRegistrationData.paket?.indexOf(e); + return Column( + children: [ + Row( + children: [ + Container( + margin: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 16), + bottom: Constant.getActualY( + context: context, y: 16)), + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 20)), + width: Constant.getActualX( + context: context, x: 1160) * + 0.9, + constraints: BoxConstraints( + minHeight: Constant.getActualY( + context: context, y: 64)), + // height: Constant.getActualY( + // context: context, y: 64), + color: Colors.white, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text(e.name ?? '', + style: Constant.body3_400( + context: context) + .copyWith( + color: + Constant.textBlack)), + SizedBox( + height: 4, + ), + Text(e.detail ?? '', + style: Constant.caption2_400( + context: context) + .copyWith( + color: + Constant.textGrey)), + ], + ), + ), + Container( + margin: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 16), + bottom: Constant.getActualY( + context: context, y: 16)), + + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 20)), + // color: Colors.red, + width: Constant.getActualX( + context: context, x: 1160) * + 0.1, + child: IconButton( + onPressed: !addOrderLoading + ? () { + // delete(e.id!, e.tab!); + deletePaket(e.id!); + } + : null, + icon: Icon(EvaIcons.trash2, + size: 24, + color: Constant.textGrey)), + ) + ], + ), + Divider() + ], + ); + }).toList(), + ), + Column( + children: tempRegistrationData.tests!.map((e) { + var index = tempRegistrationData.tests?.indexOf(e); + return Column( + children: [ + Row( + children: [ + Container( + margin: EdgeInsets.only( + top: Constant.getActualY( + context: context, y: 16), + bottom: Constant.getActualY( + context: context, y: 16)), + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 20)), + width: Constant.getActualX( + context: context, x: 1160) * + 0.9, + constraints: BoxConstraints( + minHeight: Constant.getActualY( + context: context, y: 64)), + // height: Constant.getActualY( + // context: context, y: 64), + color: Colors.white, + child: Text(e.name ?? '', + style: Constant.body3_400( + context: context) + .copyWith( + color: Constant.textBlack)), + ), + // Container( + // margin: EdgeInsets.only( + // bottom: Constant.getActualY( + // context: context, y: 20)), + // alignment: Alignment.centerLeft, + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX( + // context: context, x: 20), + // vertical: 10), + // width: Constant.getActualX( + // context: context, x: 1160) * + // 0.4, + // color: Colors.white, + // child: SizedBox( + // width: Constant.getActualX( + // context: context, x: 250), + // child: CustomTextField( + // suffixIcon: Icon(EvaIcons.calendar), + // initialValue: formatDate(e.date!), + // onChange: (value) { + // pickDateTime( + // e.tab!, + // e.id!, + // value, + // ); + // }, + // validator: validateDate, + // inputFormater: [ + // MaskTextInputFormatter( + // initialText: e.date, + // mask: '##-##-#### ##:##', + // filter: {"#": RegExp(r'[0-9]')}, + // type: MaskAutoCompletionType.eager) + // ], + + // // controller: + // // TextEditingController(text: e.date), + // // controller: searchCtr, + // isPassword: false, + // hintText: "dd-mm-yyyy hh:mm", + // labelText: "dd-mm-yyyy hh:mm", + // ), + // ), + // ), + Container( + margin: EdgeInsets.only( + bottom: Constant.getActualY( + context: context, y: 20)), + + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualX( + context: context, x: 20)), + // color: Colors.red, + width: Constant.getActualX( + context: context, x: 1160) * + 0.1, + child: IconButton( + onPressed: !addOrderLoading + ? () { + delete(e.id!, e.tab!); + } + : null, + icon: Icon(EvaIcons.trash2, + size: 24, + color: Constant.textGrey)), + ) + ], + ), + Divider() + ], + ); + }).toList(), + ), + // Container( + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX( + // context: context, x: 20)), + // child: Divider()), + // Row( + // children: [ + // Container( + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX( + // context: context, x: 20), + // vertical: Constant.getActualY( + // context: context, y: 16)), + // width: Constant.getActualX( + // context: context, x: 1160) * + // 0.34, + // child: Text( + // "TOTAL", + // style: Constant.body3_500(context: context), + // ), + // ), + // Container( + // alignment: Alignment.centerRight, + // padding: EdgeInsets.symmetric( + // horizontal: Constant.getActualX( + // context: context, x: 20), + // vertical: Constant.getActualY( + // context: context, y: 16)), + // width: Constant.getActualX( + // context: context, x: 1160) * + // 0.13, + // child: Text( + // Constant.convertToIdr( + // double.parse( + // tempRegistrationData.total ?? "0"), + // 0), + // style: Constant.body3_500(context: context), + // ), + // ) + // ], + // ) + ], + ), + ), + ), + ), + ], + ); + } +} diff --git a/linux/.gitignore b/linux/.gitignore new file mode 100644 index 0000000..d3896c9 --- /dev/null +++ b/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 0000000..9a915c2 --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,145 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "mitra_corporate") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.mitra_corporate") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000..d5bd016 --- /dev/null +++ b/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..f6f23bf --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); +} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..e0f0a47 --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000..f16b4c3 --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/main.cc b/linux/main.cc new file mode 100644 index 0000000..e7c5c54 --- /dev/null +++ b/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/my_application.cc b/linux/my_application.cc new file mode 100644 index 0000000..ad49c4f --- /dev/null +++ b/linux/my_application.cc @@ -0,0 +1,124 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "mitra_corporate"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "mitra_corporate"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/linux/my_application.h b/linux/my_application.h new file mode 100644 index 0000000..72271d5 --- /dev/null +++ b/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/macos/.gitignore b/macos/.gitignore new file mode 100644 index 0000000..746adbb --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000..5384404 --- /dev/null +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,22 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import device_info_plus +import path_provider_foundation +import pdfx +import shared_preferences_foundation +import url_launcher_macos +import webview_flutter_wkwebview + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + PdfxPlugin.register(with: registry.registrar(forPlugin: "PdfxPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) + FLTWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "FLTWebViewFlutterPlugin")) +} diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..cbdd6c9 --- /dev/null +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,705 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* mitra_corporate.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "mitra_corporate.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* mitra_corporate.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* mitra_corporate.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.mitraCorporate.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mitra_corporate.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mitra_corporate"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.mitraCorporate.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mitra_corporate.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mitra_corporate"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.mitraCorporate.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mitra_corporate.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mitra_corporate"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..9ba8380 --- /dev/null +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..8e02df2 --- /dev/null +++ b/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000..82b6f9d Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000..13b35eb Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000..0a3f5fa Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000..bdb5722 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 0000000..f083318 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000..326c0e7 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000..2f1632c Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..80e867a --- /dev/null +++ b/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..c207bba --- /dev/null +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = mitra_corporate + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.mitraCorporate + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..dddb8a3 --- /dev/null +++ b/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..3cc05eb --- /dev/null +++ b/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements new file mode 100644 index 0000000..852fa1a --- /dev/null +++ b/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/macos/RunnerTests/RunnerTests.swift b/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..61f3bd1 --- /dev/null +++ b/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +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. + } + +} diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..46f5a40 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,1007 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + age_calculator: + dependency: "direct main" + description: + name: age_calculator + sha256: "00190836e4e0ab9d11a60675a7109b36e5771bdfd4d24d7c55012d8370ecccba" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + ansicolor: + dependency: transitive + description: + name: ansicolor + sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f" + url: "https://pub.dev" + source: hosted + version: "2.0.3" + archive: + dependency: transitive + description: + name: archive + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d + url: "https://pub.dev" + source: hosted + version: "3.6.1" + args: + dependency: transitive + description: + name: args + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + back_button_interceptor: + dependency: transitive + description: + name: back_button_interceptor + sha256: "8354b03320043db546e3f446af171faaa71384100486444113628f7db1e7fe9b" + url: "https://pub.dev" + source: hosted + version: "7.0.3" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 + url: "https://pub.dev" + source: hosted + version: "3.0.5" + csslib: + dependency: transitive + description: + name: csslib + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + data_table_2: + dependency: "direct main" + description: + name: data_table_2 + sha256: f02ec9b24f44420816a87370ff4f4e533e15b274f6267e4c9a88a585ad1a0473 + url: "https://pub.dev" + source: hosted + version: "2.5.15" + date_field: + dependency: "direct main" + description: + name: date_field + sha256: "8643c8e5f2ee55b4b389fc567ee27186c5a63570f545b291d61f02ab6ca7981d" + url: "https://pub.dev" + source: hosted + version: "5.2.1" + device_info_plus: + dependency: transitive + description: + name: device_info_plus + sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074 + url: "https://pub.dev" + source: hosted + version: "10.1.2" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: "282d3cf731045a2feb66abfe61bbc40870ae50a3ed10a4d3d217556c35c8c2ba" + url: "https://pub.dev" + source: hosted + version: "7.0.1" + dio: + dependency: "direct main" + description: + name: dio + sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260" + url: "https://pub.dev" + source: hosted + version: "5.7.0" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" + eva_icons_flutter: + dependency: "direct main" + description: + name: eva_icons_flutter + sha256: "6d48a10b93590ab83eb092bee5adacdeb14f3d83f527a4b9e4092c363d56e2a8" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + extension: + dependency: transitive + description: + name: extension + sha256: be3a6b7f8adad2f6e2e8c63c895d19811fcf203e23466c6296267941d0ff4f24 + url: "https://pub.dev" + source: hosted + version: "0.6.0" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + fl_chart: + dependency: "direct main" + description: + name: fl_chart + sha256: "94307bef3a324a0d329d3ab77b2f0c6e5ed739185ffc029ed28c0f9b019ea7ef" + url: "https://pub.dev" + source: hosted + version: "0.69.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_datetime_picker: + dependency: "direct main" + description: + name: flutter_datetime_picker + sha256: "8e695c63c769350e541951227c2775190ec73ceda774a315b1dc9a99d5facfe5" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + flutter_hooks: + dependency: "direct main" + description: + name: flutter_hooks + sha256: cde36b12f7188c85286fba9b38cc5a902e7279f36dd676967106c041dc9dde70 + url: "https://pub.dev" + source: hosted + version: "0.20.5" + flutter_keyboard_visibility: + dependency: transitive + description: + name: flutter_keyboard_visibility + sha256: "98664be7be0e3ffca00de50f7f6a287ab62c763fc8c762e0a21584584a3ff4f8" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + flutter_keyboard_visibility_linux: + dependency: transitive + description: + name: flutter_keyboard_visibility_linux + sha256: "6fba7cd9bb033b6ddd8c2beb4c99ad02d728f1e6e6d9b9446667398b2ac39f08" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + flutter_keyboard_visibility_macos: + dependency: transitive + description: + name: flutter_keyboard_visibility_macos + sha256: c5c49b16fff453dfdafdc16f26bdd8fb8d55812a1d50b0ce25fc8d9f2e53d086 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + flutter_keyboard_visibility_platform_interface: + dependency: transitive + description: + name: flutter_keyboard_visibility_platform_interface + sha256: e43a89845873f7be10cb3884345ceb9aebf00a659f479d1c8f4293fcb37022a4 + url: "https://pub.dev" + source: hosted + version: "2.0.0" + flutter_keyboard_visibility_web: + dependency: transitive + description: + name: flutter_keyboard_visibility_web + sha256: d3771a2e752880c79203f8d80658401d0c998e4183edca05a149f5098ce6e3d1 + url: "https://pub.dev" + source: hosted + version: "2.0.0" + flutter_keyboard_visibility_windows: + dependency: transitive + description: + name: flutter_keyboard_visibility_windows + sha256: fc4b0f0b6be9b93ae527f3d527fb56ee2d918cd88bbca438c478af7bcfd0ef73 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_native_splash: + dependency: "direct main" + description: + name: flutter_native_splash + sha256: aa06fec78de2190f3db4319dd60fdc8d12b2626e93ef9828633928c2dcaea840 + url: "https://pub.dev" + source: hosted + version: "2.4.1" + flutter_pdfview: + dependency: "direct main" + description: + name: flutter_pdfview + sha256: "6b625b32a9102780236554dff42f2d798b4627704ab4a3153c07f2134a52b697" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + flutter_riverpod: + dependency: "direct main" + description: + name: flutter_riverpod + sha256: "0f1974eff5bbe774bf1d870e406fc6f29e3d6f1c46bd9c58e7172ff68a785d7d" + url: "https://pub.dev" + source: hosted + version: "2.5.1" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_typeahead: + dependency: "direct main" + description: + name: flutter_typeahead + sha256: d64712c65db240b1057559b952398ebb6e498077baeebf9b0731dade62438a6d + url: "https://pub.dev" + source: hosted + version: "5.2.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + helpers: + dependency: transitive + description: + name: helpers + sha256: d82a48e5acd45e3650af20779cf61326dd9c1aaf729fdab4ae721dc48f28b840 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + hooks_riverpod: + dependency: "direct main" + description: + name: hooks_riverpod + sha256: "97266a91c994951a06ef0ff3a1c7fb261e52ec7f74e87f0614ea0b7411b859b2" + url: "https://pub.dev" + source: hosted + version: "2.5.2" + html: + dependency: transitive + description: + name: html + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + url: "https://pub.dev" + source: hosted + version: "0.15.4" + http: + dependency: "direct main" + description: + name: http + sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + url: "https://pub.dev" + source: hosted + version: "0.13.6" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + image: + dependency: transitive + description: + name: image + sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" + url: "https://pub.dev" + source: hosted + version: "4.2.0" + internet_file: + dependency: "direct main" + description: + name: internet_file + sha256: c3e6aa0c1cc6c08e701bb91019a7784fece1f64e18464f53df1200caa7598b68 + url: "https://pub.dev" + source: hosted + version: "1.2.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" + jiffy: + dependency: "direct main" + description: + name: jiffy + sha256: "3497caaa36d36a29033e66803c9739ce6bccbc7e241ca46070f76ee9e6f6eb0c" + url: "https://pub.dev" + source: hosted + version: "6.3.1" + just_the_tooltip: + dependency: "direct main" + description: + name: just_the_tooltip + sha256: "7a081133d57285bfb41b331f411006d57b433d7b35772e6155745f6a7a09cb82" + url: "https://pub.dev" + source: hosted + version: "0.0.12" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + url: "https://pub.dev" + source: hosted + version: "10.0.5" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + url: "https://pub.dev" + source: hosted + version: "3.0.5" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + linear_step_indicator: + dependency: "direct main" + description: + name: linear_step_indicator + sha256: "8c3f4a7d6f8a42ba18695f4372a1a0a8ebcdea5f684b3145fc4d53bb94e3f178" + url: "https://pub.dev" + source: hosted + version: "1.0.0+3" + lints: + dependency: transitive + description: + name: lints + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + loader_overlay: + dependency: "direct main" + description: + name: loader_overlay + sha256: b87bd0c5f4a0c8249613b1c1666344469177535a6b7782b620a4ff1b8f52efa1 + url: "https://pub.dev" + source: hosted + version: "4.0.2+1" + loading_animation_widget: + dependency: "direct main" + description: + name: loading_animation_widget + sha256: ee3659035528d19145d50cf0107632bf647e7306c88b6a32f35f3bed63f6d728 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + mask_text_input_formatter: + dependency: "direct main" + description: + name: mask_text_input_formatter + sha256: "978c58ec721c25621ceb468e633f4eef64b64d45424ac4540e0565d4f7c800cd" + url: "https://pub.dev" + source: hosted + version: "2.9.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + url: "https://pub.dev" + source: hosted + version: "1.15.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + url: "https://pub.dev" + source: hosted + version: "2.1.4" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7" + url: "https://pub.dev" + source: hosted + version: "2.2.10" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + url: "https://pub.dev" + source: hosted + version: "2.4.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + pdfx: + dependency: "direct main" + description: + name: pdfx + sha256: cbbd7bf54d6f37524df85d06a816fa095d124cd32d42909effddc0027f9db10b + url: "https://pub.dev" + source: hosted + version: "2.8.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" + source: hosted + version: "6.0.2" + photo_view: + dependency: transitive + description: + name: photo_view + sha256: "1fc3d970a91295fbd1364296575f854c9863f225505c28c46e0a03e48960c75e" + url: "https://pub.dev" + source: hosted + version: "0.15.0" + platform: + dependency: transitive + description: + name: platform + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + url: "https://pub.dev" + source: hosted + version: "3.1.5" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pointer_interceptor: + dependency: transitive + description: + name: pointer_interceptor + sha256: "57210410680379aea8b1b7ed6ae0c3ad349bfd56fe845b8ea934a53344b9d523" + url: "https://pub.dev" + source: hosted + version: "0.10.1+2" + pointer_interceptor_ios: + dependency: transitive + description: + name: pointer_interceptor_ios + sha256: a6906772b3205b42c44614fcea28f818b1e5fdad73a4ca742a7bd49818d9c917 + url: "https://pub.dev" + source: hosted + version: "0.10.1" + pointer_interceptor_platform_interface: + dependency: transitive + description: + name: pointer_interceptor_platform_interface + sha256: "0597b0560e14354baeb23f8375cd612e8bd4841bf8306ecb71fcd0bb78552506" + url: "https://pub.dev" + source: hosted + version: "0.10.0+1" + pointer_interceptor_web: + dependency: transitive + description: + name: pointer_interceptor_web + sha256: "7a7087782110f8c1827170660b09f8aa893e0e9a61431dbbe2ac3fc482e8c044" + url: "https://pub.dev" + source: hosted + version: "0.10.2+1" + riverpod: + dependency: transitive + description: + name: riverpod + sha256: f21b32ffd26a36555e501b04f4a5dca43ed59e16343f1a30c13632b2351dfa4d + url: "https://pub.dev" + source: hosted + version: "2.5.1" + scroll_navigation: + dependency: "direct main" + description: + name: scroll_navigation + sha256: e252abf1a29c801154df2cee44d62a8fb70c3ca6b1da962c555bf445a6b50e7a + url: "https://pub.dev" + source: hosted + version: "1.3.2" + scroll_to_index: + dependency: "direct main" + description: + name: scroll_to_index + sha256: b707546e7500d9f070d63e5acf74fd437ec7eeeb68d3412ef7b0afada0b4f176 + url: "https://pub.dev" + source: hosted + version: "3.0.1" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f + url: "https://pub.dev" + source: hosted + version: "2.5.2" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e + url: "https://pub.dev" + source: hosted + version: "2.4.2" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + state_notifier: + dependency: transitive + description: + name: state_notifier + sha256: "8fe42610f179b843b12371e40db58c9444f8757f8b69d181c97e50787caed289" + url: "https://pub.dev" + source: hosted + version: "0.7.2+1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "51b08572b9f091f8c3eb4d9d4be253f196ff0075d5ec9b10a884026d5b55d7bc" + url: "https://pub.dev" + source: hosted + version: "3.3.0+2" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + top_snackbar_flutter: + dependency: "direct main" + description: + name: top_snackbar_flutter + sha256: "22d14664a13db6ac714934c3382bd8d4daa57fb888a672f922df71981c5a5cb2" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + universal_file: + dependency: transitive + description: + name: universal_file + sha256: d1a957fccaad2a32023b62fe435b273ee47aaf2eb804709795e4bf4afff50960 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + universal_io: + dependency: transitive + description: + name: universal_io + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + universal_platform: + dependency: transitive + description: + name: universal_platform + sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3" + url: "https://pub.dev" + source: hosted + version: "6.3.0" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: e35a698ac302dd68e41f73250bd9517fe3ab5fa4f18fe4647a0872db61bacbab + url: "https://pub.dev" + source: hosted + version: "6.3.10" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e + url: "https://pub.dev" + source: hosted + version: "6.3.1" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af + url: "https://pub.dev" + source: hosted + version: "3.2.0" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + url: "https://pub.dev" + source: hosted + version: "14.2.4" + web: + dependency: transitive + description: + name: web + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + url: "https://pub.dev" + source: hosted + version: "1.1.0" + webview_flutter: + dependency: "direct main" + description: + name: webview_flutter + sha256: ec81f57aa1611f8ebecf1d2259da4ef052281cb5ad624131c93546c79ccc7736 + url: "https://pub.dev" + source: hosted + version: "4.9.0" + webview_flutter_android: + dependency: transitive + description: + name: webview_flutter_android + sha256: "6e64fcb1c19d92024da8f33503aaeeda35825d77142c01d0ea2aa32edc79fdc8" + url: "https://pub.dev" + source: hosted + version: "3.16.7" + webview_flutter_platform_interface: + dependency: transitive + description: + name: webview_flutter_platform_interface + sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d + url: "https://pub.dev" + source: hosted + version: "2.10.0" + webview_flutter_web: + dependency: "direct main" + description: + name: webview_flutter_web + sha256: cbe1efe45e1be8470fdef7ddb75e2e2998c7ca47b75c09b9354934d20eca146b + url: "https://pub.dev" + source: hosted + version: "0.2.3+2" + webview_flutter_wkwebview: + dependency: transitive + description: + name: webview_flutter_wkwebview + sha256: "1942a12224ab31e9508cf00c0c6347b931b023b8a4f0811e5dec3b06f94f117d" + url: "https://pub.dev" + source: hosted + version: "3.15.0" + win32: + dependency: transitive + description: + name: win32 + sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a" + url: "https://pub.dev" + source: hosted + version: "5.5.4" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" + url: "https://pub.dev" + source: hosted + version: "1.1.5" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..f0751e8 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,80 @@ +name: mitra_corporate +description: "A new Flutter project." +publish_to: 'none' +version: 0.1.0 + +environment: + sdk: ^3.5.0 + +dependencies: + flutter: + sdk: flutter + flutter_localizations: # Add this line + sdk: flutter # Add this line + data_table_2: ^2.3.8 + scroll_navigation: ^1.3.2 + flutter_riverpod: ^2.5.1 + flutter_hooks: ^0.20.5 + hooks_riverpod: ^2.5.2 + linear_step_indicator: ^1.0.0+3 + equatable: ^2.0.5 + intl: ^0.19.0 + dio: ^5.1.1 + shared_preferences: ^2.2.1 + top_snackbar_flutter: ^3.1.0 + loading_animation_widget: ^1.2.0+4 + loader_overlay: ^4.0.2+1 + age_calculator: ^1.0.0 + jiffy: ^6.2.1 + flutter_datetime_picker: ^1.5.1 + flutter_pdfview: ^1.2.5 + path_provider: ^2.1.1 + flutter_typeahead: ^5.2.0 + http: ^0.13.5 + pdfx: ^2.3.0 + internet_file: ^1.2.0 + + webview_flutter_web: ^0.2.2+4 + webview_flutter: ^4.8.0 + flutter_native_splash: ^2.4.1 + fl_chart: ^0.69.0 + date_field: ^5.2.1 + mask_text_input_formatter: ^2.5.0 + just_the_tooltip: ^0.0.12 + scroll_to_index: ^3.0.1 + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + eva_icons_flutter: ^3.1.0 + url_launcher: ^6.1.11 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter_native_splash: + color: "#ffffff" + image: images/pramita_splash.png + + + + +flutter: + uses-material-design: true + assets: + - images/background_login.png + - images/logo_kedungdoro.png + - images/grafik.png + - images/registrasi_sukses.png + - images/pramita_login.png + - images/logo_pramita.png + - images/order_success.png + + fonts: + - family: OpenSans + fonts: + - asset: fonts/OpenSans-Bold.ttf + - asset: fonts/OpenSans-ExtraBold.ttf + - asset: fonts/OpenSans-SemiBold.ttf + - asset: fonts/OpenSans-Medium.ttf + - asset: fonts/OpenSans-Regular.ttf diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..8306569 --- /dev/null +++ b/web/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + mitra_corporate + + + + + + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..41d7243 --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "mitra_corporate", + "short_name": "mitra_corporate", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/web/pramita.png b/web/pramita.png new file mode 100644 index 0000000..3000eaf Binary files /dev/null and b/web/pramita.png differ diff --git a/windows/.gitignore b/windows/.gitignore new file mode 100644 index 0000000..d492d0d --- /dev/null +++ b/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt new file mode 100644 index 0000000..ff75746 --- /dev/null +++ b/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(mitra_corporate LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "mitra_corporate") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000..903f489 --- /dev/null +++ b/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..0d51764 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,17 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + PdfxPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PdfxPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); +} diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..dc139d8 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000..062f6f6 --- /dev/null +++ b/windows/flutter/generated_plugins.cmake @@ -0,0 +1,25 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + pdfx + url_launcher_windows +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt new file mode 100644 index 0000000..394917c --- /dev/null +++ b/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc new file mode 100644 index 0000000..427f45e --- /dev/null +++ b/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "mitra_corporate" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "mitra_corporate" "\0" + VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "mitra_corporate.exe" "\0" + VALUE "ProductName", "mitra_corporate" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp new file mode 100644 index 0000000..955ee30 --- /dev/null +++ b/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/windows/runner/flutter_window.h b/windows/runner/flutter_window.h new file mode 100644 index 0000000..6da0652 --- /dev/null +++ b/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp new file mode 100644 index 0000000..28a7ac1 --- /dev/null +++ b/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"mitra_corporate", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/windows/runner/resource.h b/windows/runner/resource.h new file mode 100644 index 0000000..66a65d1 --- /dev/null +++ b/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000..c04e20c Binary files /dev/null and b/windows/runner/resources/app_icon.ico differ diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest new file mode 100644 index 0000000..153653e --- /dev/null +++ b/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp new file mode 100644 index 0000000..3a0b465 --- /dev/null +++ b/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + unsigned int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/windows/runner/utils.h b/windows/runner/utils.h new file mode 100644 index 0000000..3879d54 --- /dev/null +++ b/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp new file mode 100644 index 0000000..60608d0 --- /dev/null +++ b/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h new file mode 100644 index 0000000..e901dde --- /dev/null +++ b/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_